爬虫之聚焦爬虫与验证码处理
一 . 我们先安装一个叫Anaconda的软件
参考链接: https://zhuanlan.zhihu.com/p/32925500 这里我们主要用到的是jupyter notebook,下载anaconda的原因是帮助解决jupyter的依赖关系
然后在文件夹下按住shift+右键 ->在此处打开命令窗口,输入jupyter notebook就可以了!
二 . 实现聚焦爬虫(数据解析)
编码流程:
1.指定url
2.发起请求
3.获取相应数据
4.数据解析
5.持久化存储
如何实现数据解析:
1.正则,(基本上不会用,这里不做介绍)
2.bs4
3.xpath
数据解析的原理
1.实现标签定位
2.将标签中存储的文本内容或者相关的属性值进行提取
bs4数据解析
# 环境安装
1.pip install bs4
2.pip install lxml
# 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容
(1)转化本地文件:
- soup = BeautifulSoup(open('本地文件'), 'lxml')
(2)转化网络文件:
page_text = requests.get(url, headers=headers).text
soup = BeautifulSoup(page_text , 'lxml')
(3)打印soup对象显示内容为html文件中的内容
# 调用该对象中相关的属性或者方法进行标签的定位和内容的提取
(1)根据标签名查找
- soup.a 只能找到第一个符合要求的标签
(2)获取属性
- soup.a.attrs 获取a所有的属性和属性值,返回一个字典
- soup.a.attrs['href'] 获取href属性
- soup.a['href'] 也可简写为这种形式
(3)获取内容
- soup.a.string
- soup.a.text
- soup.a.get_text()
【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容
(4)find:找到第一个符合要求的标签
- soup.find('a') 找到第一个符合要求的
- soup.find('a', title="xxx")
- soup.find('a', alt="xxx")
- soup.find('a', class_="xxx")
- soup.find('a', id="xxx")
(5)find_all:找到所有符合要求的标签
- soup.find_all('a')
- soup.find_all(['a','b']) 找到所有的a和b标签
- soup.find_all('a', limit=2) 限制前两个
(6)根据选择器选择指定的内容
select:soup.select('#feng')
- 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
- 层级选择器:
div .dudu #lala .meme .xixi 下面好多级
div > p > a > .la 只能是下面一级
div p div标签所有的p标签(子标签)
【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
示例: 爬取三国演义的章节与内容
from bs4 import BeautifulSoup
url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/70.0.3538.110 Safari/537.36'
}
page_text = requests.get(url=url, headers=headers, verify=False).text
# 实例化soup对象
soup = BeautifulSoup(page_text, 'lxml')
# 解析出章节标题和详情页的链接
li_list = soup.select('.book-mulu > ul > li')
# 持久化存储
f = open('./sanguo.txt', 'w', encoding='utf-8')
for li in li_list:
title = li.a.string
detail_url = 'http://www.shicimingju.com' + li.a['href']
# 对详情页url发起请求,进行章节内容的解析提取
detail_page_text = requests.get(url=detail_url, headers=headers, verify=False).text
# 将详情页页面内容解析提取
detail_soup = BeautifulSoup(detail_page_text, 'lxml')
div_tag = detail_soup.find('div', class_='chapter_content')
content = div_tag.text
f.write(title + ':' + content + '\n')
f.close()
xpath解析(最常用)
测试HTML页面
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>测试bs4</title>
</head>
<body>
<div>
<p>百里守约</p>
</div>
<div class="song">
<p>李清照</p>
<p>王安石</p>
<p>苏轼</p>
<p>柳宗元</p>
<a href="http://www.song.com/" title="赵匡胤" target="_self">
<span>this is span</span>
宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
<a href="" class="du">总为浮云能蔽日,长安不见使人愁</a>
<img src="http://www.baidu.com/meinv.jpg" alt="" />
</div>
<div class="tang">
<ul>
<li><a href="http://www.baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
<li><a href="http://www.163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不教胡马度阴山</a></li>
<li><a href="http://www.126.com" alt="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢君</a></li>
<li><a href="http://www.sina.com" class="du">杜甫</a></li>
<li><a href="http://www.dudu.com" class="du">杜牧</a></li>
<li><b>杜小月</b></li>
<li><i>度蜜月</i></li>
<li><a href="http://www.haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴宫花草埋幽径,晋代衣冠成古丘</a></li>
</ul>
</div>
</body>
</html>
使用姿势:
通用性比较强,不光python能用
环境安装:pip install lxml
导包:from lxml import etree
# 解析原理:
1.实例化一个etree对象,且将解析的页面源码加载到该对象中
2.使用该对象中的xpath方法结合着xpath表达式进行标签定位和数据解析提取
- 本地文件:tree = etree.parse(文件名)
tree.xpath("xpath表达式")
page_text = requests.get(url, headers=headers).text
- 网络数据:tree = etree.HTML(page_text)
tree.xpath("xpath表达式")
# xpath表达式
/:从跟标签开始实现层级定位 /html/body/div
//:从任意位置实现标签定位
属性定位:
#找到class属性值为song的div标签
//div[@class="song"]
层级&索引定位:
#找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a
//div[@class="tang"]/ul/li[2]/a
逻辑运算:
#找到href属性值为空且class属性值为du的a标签
//a[@href="" and @class="du"]
模糊匹配:
//div[contains(@class, "ng")]
//div[starts-with(@class, "ta")]
取文本:
# /表示获取某个标签下的文本内容
# //表示获取某个标签下的文本内容和所有子标签下的文本内容
//div[@class="song"]/p[1]/text() # 获取第一个p标签的文本
//div[@class="tang"]//text() # 获取div下面的所有文本内容
取属性:
//div[@class="tang"]//li[2]/a/@href
示例1:
import requests
from lxml import etree
url = 'http://pic.netbian.com/4kdongwu/'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/70.0.3538.110 Safari/537.36'
}
response = requests.get(url=url,headers=headers)
# 手动设置相应数据的编码
# response.encoding = 'utf-8'
page_text = response.text
tree = etree.HTML(page_text)
li_list = tree.xpath('//div[@class="slist"]/ul/li')
for li in li_list:
img_src = li.xpath('./a/img/@src')[0]
img_name = li.xpath('./a/b/text()')[0]
img_name = img_name.encode('iso-8859-1').decode('gbk') # 解决中文乱码问题
print(img_src,img_name)
示例2:
# 爬高清壁纸图片:http://pic.netbian.com/4kmeinv/
import requests
from lxml import etree
import time
url = 'http://pic.netbian.com/4kmeinv/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/70.0.3538.110 Safari/537.36',
}
#获取页面文本数据
response = requests.get(url=url,headers=headers)
page_text = response.text
#解析页面数据(获取页面中的图片链接)
#创建etree对象
tree = etree.HTML(page_text)
li_list = tree.xpath('//div[@class="slist"]//li')
#解析获取图片地址和图片的名称
for li in li_list:
time.sleep(0.2) # 防止频率太频繁
image_url = li.xpath('.//img/@src')
image_url_all = 'http://pic.netbian.com'+ image_url[0]
image_name = li.xpath('.//b/text()')[0]
image_name = image_name.encode('iso-8859-1').decode('gbk')
url_name = image_url_all + ' :' + image_name
print(url_name)
三 . 验证码处理
引入:
相关的门户网站在进行登录的时候,如果用户连续登录的次数超过3次或者5次的时候,就会在登录页中动态生成验证码。通过验证码达到分流和反爬的效果。
解决办法:
使用云打码平台识别验证码
云打码平台处理验证码的实现流程:
- 1.对携带验证码的页面数据进行抓取
- 2.可以将页面数据中验证码进行解析,验证码图片下载到本地
- 3.可以将验证码图片提交给三方平台进行识别,返回验证码图片上的数据值
- 云打码平台:
- 1.在官网中进行注册(普通用户和开发者用户)
- 2.登录开发者用户:
- 1.实例代码的下载(开发文档-》调用实例及最新的DLL->PythonHTTP实例下载)
- 2.创建一个软件:我的软件->添加新的软件
-3.使用示例代码中的源码文件中的代码进行修改,让其识别验证码图片中的数据值
代码展示:
# 该函数就调用了打码平台的相关的接口对指定的验证码图片进行识别,返回图片上的数据值
def getCode(codeImg):
# 云打码平台普通用户的用户名
username = 'attila666'
# 云打码平台普通用户的密码
password = 'attila666'
# 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得!
appid = 6003
# 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得!
appkey = '1f4b564483ae5c907a1d34f8e2f2776c'
# 验证码图片文件
filename = codeImg
# 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html
codetype = 3000
# 超时时间,秒
timeout = 20
# 检查
if (username == 'username'):
print('请设置好相关参数再测试')
else:
# 初始化
yundama = YDMHttp(username, password, appid, appkey)
# 登陆云打码
uid = yundama.login();
print('uid: %s' % uid)
# 查询余额
balance = yundama.balance();
print('balance: %s' % balance)
# 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果
cid, result = yundama.decode(filename, codetype, timeout);
print('cid: %s, result: %s' % (cid, result))
return result