Day7.数据采集-爬虫

爱被打了一巴掌 2023-02-14 13:13 30阅读 0赞

数据采集

我们进行数据分析以及挖掘时,前提条件就是需要有数据;如果在公司里作业,我们可以从数据库中导入数据,但同时我们也可以对采集数据来进行分析。采集数据最常用就是我们听到的爬虫,通过爬虫爬取网页上的信息,如购物网站用户评论进行产品调研,微博留言等来进行舆论分析,那么今天我就来了解如何使用爬虫采集数据。

format_png

通过爬虫获取数据,我们可以有两种方式,一个是通过抓取软件工具,如:火车采集器、八爪鱼、集搜客等,这里推荐使用八爪鱼,它可以提供一个免费的版本使用;还有一种方式是通过Python编程抓取网页信息。

八爪鱼采集数据

八爪⻥的使用简便,提供图形化的界面,基本上不需要编写代码,除了在正则表达式匹配的时候会用到XPath。XPath的英文是XML Path Language,也就是XML的路径语言,用来在XML文件中寻找我们想要的元素,xml文件用来存放描述和存放数据,因而八爪⻥可以使用XPath帮我们更灵活地定位我们想要找的元素。

在百度搜索下载”八爪鱼采集器“下载安装,官方在教程与帮助中提供的内容也非常丰富,在这里我们就不演示了,需要使用的时候我们根据官网教程学习,上手简单。基本流程是输入网页,设计流程和启动采集。在流程设计中设置好关键词,软件搜索后,设置翻页并提取数据,启动采集;此外八爪鱼还提供很多模板,帮助快速设置需要爬取的内容。

Python爬虫

使用八爪鱼采集器虽然上手速度快,但是也存在一些问题,比如运行速度慢、定制化程度不高,通过爬虫可以解决这一问题。

爬虫实际上是用浏览器访问的方式模拟了访问网站的过程,整个过程包括三个阶段: 打开网⻚、提取数据和保存数据。三个阶段都有对应的工具可以使用。在“打开网⻚”这一步骤中,可以使用 Requests 库访问⻚面,得到服务器返回给我们的数据,这里包括HTML⻚面以及JSON数据。“提取数据”这一步骤中,主要用到了两个工具;针对HTML⻚面,可以使用 XPath 进行元素定位,提取数据;针对JSON数据,可以使用JSON进行解析。在最后一步“保存数据”中,使用 Pandas 保存数据,导出CSV文件。下面我来介绍下这些工具的使用:

Requests访问⻚面

Requests是Python HTTP的客户端库,编写爬虫的时候都会用到,编写起来也很简单。它有两种访问方式:Get和Post。这两者最直观的区别就是:Get把参数包含在url中,而Post通过request body来传递参数。

假设我们想访问淘宝,那么用Get访问的话,代码可以写成下面这样的:

  1. r = requests.get('http://www.taobao.com')
  2. # 代码里的“r”就是Get请求后的访问结果,然后我们可以使用r.text或r.content来获取HTML的正文

如果我们想要使用Post进行表单传递,代码就可以这样写:

  1. r = requests.post('http://xxx.com', data = {'key':'value'})
  2. # data是传递的表单参数,data的数据类型是字典

XPath定位

XPath是XML的路径语言,实际上是通过元素和属性进行导航,帮我们定位位置。它有几种常用的路径表达方式。










































表达式 含义
node 选取node节点的所有子节点
/ 从根节点选取
// 选区所有当前节点,不考虑他们的位置
. 当前节点
.. 父节点
@ 属性选择
| 或,两个节点的合计
text() 当前路径下的文本内容

一些简单的例子:

  • 1.xpath(‘node’) 选取了node节点的所有子节点;
  • 2.xpath(’/div’) 从根节点上选取div节点;
  • 3.xpath(’//div’) 选取所有的div节点;
  • 4.xpath(’./div’) 选取当前节点下的div节点;
  • 5.xpath(’..’) 回到上一个节点;
  • 6.xpath(’//@id’) 选取所有的id属性;
  • 7.xpath(’//book[@id]’) 选取所有拥有名为id的属性的book元素;
  • 8.xpath(’//book[@id=“abc”]’) 选取所有book元素,且这些book元素拥有id= “abc”的属性;
  • 9.xpath(’//book/title | //book/price’) 选取book元素的所有title和price元素。

XPath可以提供超过100个内建函数,来做匹配。网页上定位的节点,几乎都可以使用XPath来选择。使用XPath定位,会用到Python的一个解析库lxml。这个库的解析效率非常高,使用起来也很简便,只需要调用HTML解析命令即可,然后再对HTML进行XPath函数的调用。

比如我们想要定位到HTML中的所有列表项目,可以采用下面这段代码:

  1. from lxml import etree
  2. html = etree.HTML(html)
  3. result = html.xpath('//li')

HTML页面中涉及到的元素如列表List缩写是li;nide,div也是html中的知识。爬取网页需要我们对网页的知识有一定的了解,学习起来也不难。对于HTML的知识不会涉及,如果需要使用以后我可以再搜索相关知识进行学习,主体是了解以及掌握基本的爬虫知识。

JSON数据

JSON是一种轻量级的交互方式,在Python中有JSON库,可以让我们将Python对象和JSON对象进行转换。为什么要转换呢?原因也很简单。将JSON对象转换成为Python对象,我们对数据进行解析就更方便了。


















方法 含义
json.dumps() 将Python对象转换成Json对象
json.loads() 将Json对象转换成Python对象

这是一段将JSON格式转换成Python对象的代码,可以运行下这个程序的结果。

  1. import json
  2. jsonData = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
  3. input = json.loads(jsonData)
  4. print(input)

format_png 1

案例实操:

爬取公开手机型号 机型价位数据。

用于后续分析 手机性能特点。

再次强调,本案例仅限学习分享。不用于任何商业用途。

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. __author__ = 'JackFeng'
  4. # @Time : 2020/6/1 23:56
  5. # @Author : JackFeng
  6. # @FileName: PySpiderPhone.py
  7. # @Software: PyCharm
  8. # @Blog :http://www.a2data.cn/
  9. import requests
  10. from bs4 import BeautifulSoup
  11. import re
  12. import time
  13. import random
  14. """
  15. 爬取中关村网站 手机型号价位 分析手机性能
  16. 1、公开网站
  17. 2、本爬虫仅限于学习
  18. 3、不做任何商业用途
  19. 4、任何利益与本文无关
  20. """
  21. headers = {
  22. 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
  23. }
  24. try:
  25. with open('./DataScience.txt', 'a', encoding='utf-8') as file:
  26. for y in range(0,29):
  27. url = 'http://detail.zol.com.cn/cell_phone_advSearch/subcate57_1_s8379-s8010_1_1__{}.html'.format(str(y))
  28. print(url)
  29. data = requests.get(url,timeout=10).text # 下载网页的text内容
  30. soup = BeautifulSoup(data,'html.parser') # lxml 所有手机列表
  31. # print(ss_1)
  32. time.sleep(random.randint(0, 8))
  33. header='http://detail.zol.com.cn'
  34. for j in range(0,30):
  35. link_a = soup.find("div",class_='list_box').find("ul",class_='result_list').find_all('dl',class_='pro_detail')[j]
  36. index=link_a.find('dt').find('a').attrs['href']
  37. phone_url=header+index
  38. # print(phone_url)
  39. data2 = requests.get(phone_url, timeout=10).text # 下载网页的text内容
  40. soup2 = BeautifulSoup(data2, 'html.parser') # lxml 每个手机详情页面
  41. name=soup2.find('div',class_='product-model page-title clearfix').find('h1').get_text()
  42. # print(name)
  43. try:
  44. model=soup2.find('div', class_='product-model page-title clearfix').find('h2').get_text().replace("别名:","")
  45. except:
  46. model='None'
  47. # print(model)
  48. try:
  49. try:
  50. price = soup2.find('div', class_='product-price-box').find('b',class_='price-type').get_text()
  51. except:
  52. price = soup2.find('div', class_='product-detail').find('b', class_='price-type').get_text()
  53. except:
  54. price = soup2.find('div', class_='pro-zhibo-infos').find('span',class_='price-type').get_text()
  55. # print(price)
  56. print("{},name:{},model:{},price:{}".format(j,name,model,price))
  57. # 详细参数列表链接
  58. if price in ('概念产品','即将上市'): # 这类手机上市之后再爬
  59. pass
  60. else:
  61. try:
  62. try:
  63. url_param=soup2.find('div', class_='info-list-01').find('a',class_='p-more').attrs['href']
  64. except:
  65. url_param = soup2.find('div', class_='info-list-02').find('a', class_='p-more').attrs['href']
  66. except:
  67. url_param = soup2.find('div', class_='p-content').find('a',class_='_j_MP_more p-more').attrs['href']
  68. url_param=header+url_param
  69. # print(url_param)
  70. data_param = requests.get(url_param, timeout=10).text # 下载参数表格所在页面内容
  71. soup3 = BeautifulSoup(data_param, 'html.parser') # lxml
  72. table0=soup3.find('div',class_='detailed-parameters').find_all('table')[0]
  73. param_list0=table0.find_all('tr')
  74. # print(param_list0)
  75. for i in param_list0:
  76. try:
  77. head=i.find('th').find('span').get_text()
  78. # print(head)
  79. if head in ('发布会时间','上市日期'):
  80. sale_time = i.find('td').find('span').get_text()
  81. # print("{}:{}".format(head,sale_time))
  82. elif head in ('操作系统','出厂系统内核'):
  83. os = i.find('td').find('span').get_text()
  84. # print("{}:{}".format(head,os))
  85. elif head in ('手机类型'):
  86. phone_type = i.find('td').find('span').get_text()
  87. # print("{}:{}".format(head,phone_type))
  88. else:
  89. pass
  90. except:
  91. pass
  92. try:
  93. print(os)
  94. except:
  95. os='None'
  96. table1 = soup3.find('div', class_='detailed-parameters').find_all('table')[1]
  97. param_list1 = table1.find_all('tr')
  98. for i in param_list1:
  99. try:
  100. head = i.find('th').find('a').get_text()
  101. if head =='主屏尺寸':
  102. pattern = re.compile(r'\d*\S\d*(英寸)')
  103. si = i.find('td').find("span").get_text()
  104. size = re.search(pattern, si).group(0)
  105. # print("{}:{}".format(head,size))
  106. elif head=='主屏分辨率':
  107. pattern = re.compile(r'\d*\S\d*(像素)')
  108. reso = i.find('td').find("span").get_text()
  109. resolution=re.search(pattern, reso).group(0)
  110. # print("{}:{}".format(head,resolution))
  111. except:
  112. pass
  113. table2 = soup3.find('div', class_='detailed-parameters').find_all('table')[2]
  114. param_list2 = table2.find_all('tr')
  115. for i in param_list2:
  116. try:
  117. head = i.find('th').find('a').get_text()
  118. pattern = re.compile(r'\d*(GB)')
  119. if head =='RAM容量':
  120. ram1 = i.find('td').find("span").get_text()
  121. ram = re.search(pattern, ram1).group(0)
  122. # print("{}:{}".format(head,ram))
  123. elif head=='ROM容量':
  124. rom1 = i.find('td').find("span").get_text()
  125. rom = re.search(pattern, rom1).group(0)
  126. # print("{}:{}".format(head,rom))
  127. except:
  128. pass
  129. try:
  130. print(ram)
  131. except:
  132. ram='None'
  133. try:
  134. print(rom)
  135. except:
  136. rom = 'None'
  137. print("手机:{}\t机型:{}\t价格:{}\t上市时间:{}\t操作系统:{}\t屏幕尺寸:{}\t像素:{}\t运行内存:{}\t手机内存:{}\t手机类型:{}\n".format(name,model,price,sale_time,os,size,resolution,ram,rom,phone_type))
  138. file.write('手机:{}\t机型:{}\t价格:{}\t上市时间:{}\t操作系统:{}\t屏幕尺寸:{}\t像素:{}\t运行内存:{}\t手机内存:{}\t手机类型:{}\n'.format(name,model,price,sale_time,os,size,resolution,ram,rom,phone_type))
  139. print("===========第 {} 个机型信息获取完毕=========".format(j))
  140. # time.sleep(random.randint(0, 8))
  141. print("============第 {} 页信息爬取完毕==============".format(y))
  142. print("******************************所有信息爬取完毕***********************************")
  143. file.close()
  144. except requests.exceptions.ConnectionError as e:
  145. print('Error', e.args)

运行结果如下

format_png 2

format_png 3

format_png 4

写在后面:

今天的爬虫是不是很有意思~

如果有问题,但是也请耐心的调试~

记得打卡,在群里分享你的代码和笔记~

换一种方式能不能实现呢?

format_png 5

  1. 好文章,我在看❤

发表评论

表情:
评论列表 (有 0 条评论,30人围观)

还没有评论,来说两句吧...

相关阅读