Day7.数据采集-爬虫
数据采集
我们进行数据分析以及挖掘时,前提条件就是需要有数据;如果在公司里作业,我们可以从数据库中导入数据,但同时我们也可以对采集数据来进行分析。采集数据最常用就是我们听到的爬虫,通过爬虫爬取网页上的信息,如购物网站用户评论进行产品调研,微博留言等来进行舆论分析,那么今天我就来了解如何使用爬虫采集数据。
通过爬虫获取数据,我们可以有两种方式,一个是通过抓取软件工具,如:火车采集器、八爪鱼、集搜客等,这里推荐使用八爪鱼,它可以提供一个免费的版本使用;还有一种方式是通过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访问的话,代码可以写成下面这样的:
r = requests.get('http://www.taobao.com')
# 代码里的“r”就是Get请求后的访问结果,然后我们可以使用r.text或r.content来获取HTML的正文
如果我们想要使用Post进行表单传递,代码就可以这样写:
r = requests.post('http://xxx.com', data = {'key':'value'})
# 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中的所有列表项目,可以采用下面这段代码:
from lxml import etree
html = etree.HTML(html)
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对象的代码,可以运行下这个程序的结果。
import json
jsonData = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
input = json.loads(jsonData)
print(input)
案例实操:
爬取公开手机型号 机型价位数据。
用于后续分析 手机性能特点。
再次强调,本案例仅限学习分享。不用于任何商业用途。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'JackFeng'
# @Time : 2020/6/1 23:56
# @Author : JackFeng
# @FileName: PySpiderPhone.py
# @Software: PyCharm
# @Blog :http://www.a2data.cn/
import requests
from bs4 import BeautifulSoup
import re
import time
import random
"""
爬取中关村网站 手机型号价位 分析手机性能
1、公开网站
2、本爬虫仅限于学习
3、不做任何商业用途
4、任何利益与本文无关
"""
headers = {
'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',
}
try:
with open('./DataScience.txt', 'a', encoding='utf-8') as file:
for y in range(0,29):
url = 'http://detail.zol.com.cn/cell_phone_advSearch/subcate57_1_s8379-s8010_1_1__{}.html'.format(str(y))
print(url)
data = requests.get(url,timeout=10).text # 下载网页的text内容
soup = BeautifulSoup(data,'html.parser') # lxml 所有手机列表
# print(ss_1)
time.sleep(random.randint(0, 8))
header='http://detail.zol.com.cn'
for j in range(0,30):
link_a = soup.find("div",class_='list_box').find("ul",class_='result_list').find_all('dl',class_='pro_detail')[j]
index=link_a.find('dt').find('a').attrs['href']
phone_url=header+index
# print(phone_url)
data2 = requests.get(phone_url, timeout=10).text # 下载网页的text内容
soup2 = BeautifulSoup(data2, 'html.parser') # lxml 每个手机详情页面
name=soup2.find('div',class_='product-model page-title clearfix').find('h1').get_text()
# print(name)
try:
model=soup2.find('div', class_='product-model page-title clearfix').find('h2').get_text().replace("别名:","")
except:
model='None'
# print(model)
try:
try:
price = soup2.find('div', class_='product-price-box').find('b',class_='price-type').get_text()
except:
price = soup2.find('div', class_='product-detail').find('b', class_='price-type').get_text()
except:
price = soup2.find('div', class_='pro-zhibo-infos').find('span',class_='price-type').get_text()
# print(price)
print("{},name:{},model:{},price:{}".format(j,name,model,price))
# 详细参数列表链接
if price in ('概念产品','即将上市'): # 这类手机上市之后再爬
pass
else:
try:
try:
url_param=soup2.find('div', class_='info-list-01').find('a',class_='p-more').attrs['href']
except:
url_param = soup2.find('div', class_='info-list-02').find('a', class_='p-more').attrs['href']
except:
url_param = soup2.find('div', class_='p-content').find('a',class_='_j_MP_more p-more').attrs['href']
url_param=header+url_param
# print(url_param)
data_param = requests.get(url_param, timeout=10).text # 下载参数表格所在页面内容
soup3 = BeautifulSoup(data_param, 'html.parser') # lxml
table0=soup3.find('div',class_='detailed-parameters').find_all('table')[0]
param_list0=table0.find_all('tr')
# print(param_list0)
for i in param_list0:
try:
head=i.find('th').find('span').get_text()
# print(head)
if head in ('发布会时间','上市日期'):
sale_time = i.find('td').find('span').get_text()
# print("{}:{}".format(head,sale_time))
elif head in ('操作系统','出厂系统内核'):
os = i.find('td').find('span').get_text()
# print("{}:{}".format(head,os))
elif head in ('手机类型'):
phone_type = i.find('td').find('span').get_text()
# print("{}:{}".format(head,phone_type))
else:
pass
except:
pass
try:
print(os)
except:
os='None'
table1 = soup3.find('div', class_='detailed-parameters').find_all('table')[1]
param_list1 = table1.find_all('tr')
for i in param_list1:
try:
head = i.find('th').find('a').get_text()
if head =='主屏尺寸':
pattern = re.compile(r'\d*\S\d*(英寸)')
si = i.find('td').find("span").get_text()
size = re.search(pattern, si).group(0)
# print("{}:{}".format(head,size))
elif head=='主屏分辨率':
pattern = re.compile(r'\d*\S\d*(像素)')
reso = i.find('td').find("span").get_text()
resolution=re.search(pattern, reso).group(0)
# print("{}:{}".format(head,resolution))
except:
pass
table2 = soup3.find('div', class_='detailed-parameters').find_all('table')[2]
param_list2 = table2.find_all('tr')
for i in param_list2:
try:
head = i.find('th').find('a').get_text()
pattern = re.compile(r'\d*(GB)')
if head =='RAM容量':
ram1 = i.find('td').find("span").get_text()
ram = re.search(pattern, ram1).group(0)
# print("{}:{}".format(head,ram))
elif head=='ROM容量':
rom1 = i.find('td').find("span").get_text()
rom = re.search(pattern, rom1).group(0)
# print("{}:{}".format(head,rom))
except:
pass
try:
print(ram)
except:
ram='None'
try:
print(rom)
except:
rom = 'None'
print("手机:{}\t机型:{}\t价格:{}\t上市时间:{}\t操作系统:{}\t屏幕尺寸:{}\t像素:{}\t运行内存:{}\t手机内存:{}\t手机类型:{}\n".format(name,model,price,sale_time,os,size,resolution,ram,rom,phone_type))
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))
print("===========第 {} 个机型信息获取完毕=========".format(j))
# time.sleep(random.randint(0, 8))
print("============第 {} 页信息爬取完毕==============".format(y))
print("******************************所有信息爬取完毕***********************************")
file.close()
except requests.exceptions.ConnectionError as e:
print('Error', e.args)
运行结果如下
写在后面:
今天的爬虫是不是很有意思~
如果有问题,但是也请耐心的调试~
记得打卡,在群里分享你的代码和笔记~
换一种方式能不能实现呢?
好文章,我在看❤
还没有评论,来说两句吧...