爬虫
- http:
- 概念:clinet和Server进行数据交互的某种形式
- 常用的头信息:
- User-Agent:请求载体的身份标识
- Connection:close
- content-type:
- https:
- 概念:安全的http协议
- 证书
- 对称秘钥加密
- 非对称秘钥加密
- 证书秘钥加密方式
爬虫相关概述
- 爬虫概念:
- 通过编写程序模拟浏览器上网,然后让其去互联网上爬取/抓取数据的过程
- 模拟:浏览器就是一款纯天然的原始的爬虫工具
- 通过编写程序模拟浏览器上网,然后让其去互联网上爬取/抓取数据的过程
- 爬虫分类:
- 通用爬虫:爬取一整张页面中的数据. 抓取系统(爬虫程序)
- 聚焦爬虫:爬取页面中局部的数据.一定是建立在通用爬虫的基础之上
- 增量式爬虫:用来监测网站数据更新的情况.以便爬取到网站最新更新出来的数据
-
风险分析
- 合理的的使用
- 爬虫风险的体现:
- 爬虫干扰了被访问网站的正常运营;
- 爬虫抓取了受到法律保护的特定类型的数据或信息。
- 避免风险:
- 严格遵守网站设置的robots协议;
- 在规避反爬虫措施的同时,需要优化自己的代码,避免干扰被访问网站的正常运行;
- 在使用、传播抓取到的信息时,应审查所抓取的内容,如发现属于用户的个人信息、隐私或者他人的商业秘密的,应及时停止并删除。
-
反爬机制
-
反反爬策略
-
robots.txt协议:文本协议,在文本中指定了可爬和不可爬的数据说明.
- requests模块
- 概念:一个机遇网络请求的模块.作用就是用来模拟浏览器发起请求.
- 编码流程:
- 指定url
- 进行请求的发送
- 获取响应数据(爬取到的数据)
- 持久化存储
- 环境的安装:
- pip install requests
- 爬取搜狗首页对应的页面源码数据
import requests
#step_1
url = 'https://www.sogou.com'
#step_2:返回值是一个响应对象
response = requests.get(url=url)
#step_3:text返回的是字符串形式的响应数据
page_text = response.text
#step_4
with open('./sogou.html','w',encoding='utf-8') as fp:
fp.write(page_text)
- 基于搜狗编写一个简易的网页采集器
wd = input('enter a key:')
url = 'https://www.sogou.com/web'
#存储的就是动态的请求参数
params = {
'query':wd
}
#一定需要将params作用到请求中
#params参数表示的是对请求url参数的封装
response = requests.get(url=url,params=params)
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
fp.write(page_text)
print(wd,'下载成功!')
- 上述程序出现了问题
- 问题1:爬取到的数据出现了乱码
- 问题2:遇到了UA检测这种反爬机制
#解决中文乱码
wd = input('enter a key:')
url = 'https://www.sogou.com/web'
#存储的就是动态的请求参数
params = {
'query':wd
}
#一定需要将params作用到请求中
#params参数表示的是对请求url参数的封装
response = requests.get(url=url,params=params)
#手动修改响应数据的编码
response.encoding = 'utf-8'
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
fp.write(page_text)
print(wd,'下载成功!')
- 反爬机制:UA检测
- 反反爬机制:UA伪装
#解决中文乱码&UA伪装
wd = input('enter a key:')
url = 'https://www.sogou.com/web'
#存储的就是动态的请求参数
params = {
'query':wd
}
#即将发起请求对应的头信息
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
#一定需要将params作用到请求中
#params参数表示的是对请求url参数的封装
#headers参数是用来实现UA伪装
response = requests.get(url=url,params=params,headers=headers)
#手动修改响应数据的编码
response.encoding = 'utf-8'
page_text = response.text
fileName = wd+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
fp.write(page_text)
print(wd,'下载成功!')
- 爬取豆瓣电影的电影详情数据
- 分析:当滚轮滑动到底部的时候,发起了一个ajax的请求,且该请求请求到了一组电影数据
- 动态加载的数据:就是通过另一个额外的请求请求到的数据
- ajax生成动态加载的数据
- js生成动态加载的数据
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
url = 'https://movie.douban.com/j/chart/top_list'
start = input('enter a start:')
limit = input('enter a limit')
#处理请求参数
params = {
'type': '5',
'interval_id': '100:90',
'action': '',
'start': start,
'limit': limit,
}
response = requests.get(url=url,params=params,headers=headers)
#json返回的是序列化好的对象
data_list = response.json()
fp = open('douban.txt','w',encoding='utf-8')
for dic in data_list:
name = dic['title']
score = dic['score']
fp.write(name+':'+score+'\n')
print(name,'爬取成功')
fp.close()
post_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
city = input('enter a city name:')
data = {
'cname': '',
'pid': '',
'keyword': city,
'pageIndex': '3',
'pageSize': '2',
}
#data参数表示的就是get方法中的params
response = requests.post(url=post_url,data=data,headers=headers)
response.json()
思考:如何判定一张页面中是否存在动态加载的数据¶
- 什么是Anaconda
- 集成环境:基于数据分析和机器学习的开发环境
- jupyter:超级终端.就是Anaconda集成环境中提供的一种基于浏览器的可视化开发工具
- cell都是有两种常用的模式
- Code:编写python程序
- markdown:编写笔记
快捷键
- 插入cell:a,b
- 删除cell:x
- 切换cell的模式:y,m
- 执行:sihfit+enter
- tab:
- shift+tab:打开帮助文档
- 什么是Anaconda
- 集成环境:基于数据分析和机器学习的开发环境
- jupyter:超级终端.就是Anaconda集成环境中提供的一种基于浏览器的可视化开发工具
- cell都是有两种常用的模式
- Code:编写python程序
- markdown:编写笔记
快捷键
- 插入cell:a,b
- 删除cell:x
- 切换cell的模式:y,m
- 执行:sihfit+enter
- tab:
- shift+tab:打开帮助文档
import urllib.request
import urllib.parse
url = 'http://news.baidu.com/'
response = urllib.request.urlopen(url=url)
data = response.read().decode()
with open("./news.html","w") as f:
f.write(data)
print("写入文件完毕")
import requests
url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList"
fp = open('./company_detail.txt','w',encoding="utf-8")
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
for i in range(1,6):
data = {
"on": "true",
"page": str(i),
"pageSize": "15",
"productName":"",
"conditionType": "1",
"applyname":"",
"applysn":"",
}
data_dic = requests.post(url=url,data=data,headers=headers).json()
for dic in data_dic['list']:
_id = dic['ID']
# print(_id)
post_url = "http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById"
post_data = {
"id":_id
}
detail_dic = requests.post(url=post_url,data=post_data,headers=headers).json()
company_title = detail_dic['epsName']
address = detail_dic['epsProductAddress']
fp.write(company_title+":"+address+"\n")
print("已经爬取了:",company_title)
fp.close()
正则实现爬取
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
url = "https://pic.qiushibaike.com/system/pictures/12217/122176396/medium/OM37E794HBL3OFFF.jpg"
img_data = requests.get(url=url,headers=headers).content
with open('./123.jpg','wb') as f:
f.write(img_data)
回顾
- 爬虫
- 爬虫的分类:
- 通用
- 聚焦
- 增量式:监测
- 反爬机制
- 反反爬策略
- robots,UA监测:UA伪装
- http&https概念:服务器和客户端进行数据交互的某种形式
- 常用的头信息:
- User-Agent:请求载体的身份标识
- Connection:close
- content-type
- https的加密方式:证书秘钥加密
- 证书:是被应用在https的加密操作中的.该证书是有证书认证机构颁布的,证书中包含了公钥(加密方式)
- requests:
- get/post:
- url
- data/params:对请求参数的封装
- headers:UA伪装
- 什么是动态加载的数据:由另一个额外的请求请求到的数据
- ajax
- js
- 如何鉴定页面中是否有动态加载的数据?
- 局部搜索
- 全局搜索
- 对一个陌生网站进行爬取前的第一步做什么?
- 确定你要爬取的数据是否为动态加载的!!!
- get/post:
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
fp = open('./company_detail.txt','w',encoding='utf-8')
for page in range(1,6):
#要请求到没一家企业对应的id
url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList'
data = {
'on': 'true',
'page': str(page),
'pageSize': '15',
'productName': '',
'conditionType': '1',
'applyname': '',
'applysn': '',
}
#该json()的返回值中就有每一家企业的id
data_dic = requests.post(url=url,data=data,headers=headers).json()
#解析id
for dic in data_dic['list']:
_id = dic['ID']
# print(_id)
#对每一个id对应的企业详情数据进行捕获(发起请求)
post_url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById'
post_data = {
'id':_id
}
#json的返回值是某一家企业的详情信息
detail_dic = requests.post(url=post_url,data=post_data,headers=headers).json()
company_title = detail_dic['epsName']
address = detail_dic['epsProductAddress']
fp.write(company_title+':'+address+'\n')
print(company_title,'爬取成功!!!')
fp.close()
数据解析
- 解析:根据指定的规则对数据进行提取
- 作用:实现聚焦爬虫
- 聚焦爬虫的编码流程:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
- 数据解析的方式:
- 正则
- bs4
- xpath
- pyquery(拓展)
- 数据解析的通用原理是什么?
- 数据解析需要作用在页面源码中(一组html标签组成的)
- html的核心作用是什么?
- 展示数据
- html是如何展示数据的呢?
- html所要展示的数据一定是被放置在html标签之中,或者是在属性中.
- 通用原理:
- 1.标签定位
- 2.取文本or取属性
正则实现的数据解析
- 需求:爬取糗事百科中糗图数据
- 如何爬取图片数据
- 如何爬取图片数据
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
#方式1:
url = 'https://pic.qiushibaike.com/system/pictures/12217/122176396/medium/OM37E794HBL3OFFF.jpg'
img_data = requests.get(url=url,headers=headers).content #content返回的是byte类型的数据
with open('./123.jpg','wb') as fp:
fp.write(img_data)
#方式2:
from urllib import request
url = 'https://pic.qiushibaike.com/system/pictures/12217/122176396/medium/OM37E794HBL3OFFF.jpg'
request.urlretrieve(url,'./456.jpg')
- 方式1和方式2对于图片数据爬取的操作最大的不同之处是在哪?
- 方式2不可以使用UA伪装的机制
- urllib就是一个比较老的网络请求的模块,在requests模块没有出现之前,请求发送的操作使用的都是urllib
#糗事百科
import re
import os
dir_name = './qiutuLibs'
if not os.path.exists(dir_name):
os.mkdir(dir_name)
url = 'https://www.qiushibaike.com/pic/'
page_text = requests.get(url,headers=headers).text
#数据解析:图片地址
ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
img_src_list = re.findall(ex,page_text,re.S)
for src in img_src_list:
src = 'https:'+src
img_name = src.split('/')[-1]
img_path = dir_name+'/'+img_name
#对图片地址单独发起请求获取图片数据
request.urlretrieve(src,img_path)
print(img_name,'下载成功!!!')
- 爬取多页
- 分析:每一个页码对应的url是有共性:https://www.qiushibaike.com/pic/page/%d/
dir_name = './qiutuLibs'
if not os.path.exists(dir_name):
os.mkdir(dir_name)
#指定一个通用的url模板(不可变)
url = 'https://www.qiushibaike.com/pic/page/%d/'
for page in range(1,5):
print('正在爬取第{}页的图片'.format(page))
#形成一个某页码完整的url
new_url = format(url%page)
page_text = requests.get(new_url,headers=headers).text
#数据解析:图片地址
ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
img_src_list = re.findall(ex,page_text,re.S)
for src in img_src_list:
src = 'https:'+src
img_name = src.split('/')[-1]
img_path = dir_name+'/'+img_name
#对图片地址单独发起请求获取图片数据
request.urlretrieve(src,img_path)
# print(img_name,'下载成功!!!')
bs4解析
- 环境的安装:
- pip install bs4
- pip install lxml
- bs4的解析原理
- 实例化一个BeautifulSoup的对象,并且将即将被解析的页面源码数据加载到该对象中
- 调用BeautifulSoup对象中的相关属性和方法进行标签定位和数据提取
- 如何实例化BeautifulSoup对象呢?
- BeautifulSoup(fp,'lxml'):专门用作于解析本地存储的html文档中的数据
- BeautifulSoup(page_text,'lxml'):专门用作于将互联网上请求到的页面源码数据进行解析
标签定位
- soup.tagName:定位到第一个TagName标签,返回的是单数
- 属性定位:soup.find('tagName',attrName='value'),返回也是单数
- find_all:和find用法一致,但是返回值是列表
- 选择器定位:select('选择器'),返回值为列表
- 标签,类,id,层级(>:一个层级,空格:多个层级)
提取数据
- 取文本:
- tag.string:标签中直系的文本内容
- tag.text:标签中所有的文本内容
- 取属性:
- tag['attrName']
from bs4 import BeautifulSoup
fp = open('./test.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml') #将即将被解析的页面源码加载到该对象中
soup.p
soup.find('div',class_='song')
soup.find_all('div',class_='song')
soup.select('.tang')
soup.select('#feng')
soup.select('.tang > ul > li')
soup.select('.tang li')
li_6 = soup.select('.tang > ul > li')[6]
i_tag = li_6.i
i_tag.string
soup.find('div',class_='tang').text
soup.find('a',id="feng")['href']
- 爬取三国演义整篇小说内容http://www.shicimingju.com/book/sanguoyanyi.html
- 章节名称
- 章节内容
#在首页中解析章节名称&每一个章节详情页的url
url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
page_text = requests.get(url,headers=headers).text
soup = BeautifulSoup(page_text,'lxml')
a_list = soup.select('.book-mulu > ul > li > a')
fp = open('sanguo.txt','w',encoding='utf-8')
for a in a_list:
detail_url = 'http://www.shicimingju.com'+a['href']
chap_title = a.string
#对章节详情页的url发起请求,解析详情页中的章节内容
detail_page_text = requests.get(detail_url,headers=headers).text
soup = BeautifulSoup(detail_page_text,'lxml')
chap_content = soup.find('div',class_="chapter_content").text
fp.write(chap_title+':'+chap_content+'\n')
print(chap_title,'爬取成功!')
fp.close()
xpath解析
- 环境的安装:pip install lxml
- xpath的解析原理
- 实例化一个etree类型的对象,且将页面源码数据加载到该对象中
- 需要调用该对象的xpath方法结合着不同形式的xpath表达式进行标签定位和数据提取
- etree对象的实例化
- etree.parse(fileNane)
- etree.HTML(page_text)
- xpath方法返回的永远是一个列表
标签定位
- 在xpath表达式中最最侧的/表示的含义是说,当前定位的标签必须从根节点开始进行定位
- xpath表达式中最左侧的//表示可以从任意位置进行标签定位
- xpath表达式中非最左侧的//表示的是多个层级的意思
- xpath表达式中非最左侧的/表示的是一个层级的意思
- 属性定位://tagName[@arrtName='value']
- 索引定位://tagName/li[3]
提取数据
- 取文本:
- /text():取直系的文本内容
- //text():取所有的文本内容
- 取属性:
- tag/@attrName
from lxml import etree
tree = etree.parse('./test.html')
tree.xpath('/html/head/meta')[0] #绝对路径
tree.xpath('//meta')[0] #相对路径,将整个页面源码中所有的meta进行定位
tree.xpath('/html//meta')[0]
#属性定位
tree.xpath('//div[@class="song"]')
#索引定位
tree.xpath('//div[@class="tang"]/ul/li[3]') #该索引是从1开始
tree.xpath('//div[@class="tang"]//li[3]') #该索引是从1开始
#取文本
tree.xpath('//p[1]/text()')
tree.xpath('//div[@class="song"]//text()')
#取属性
tree.xpath('//a[@id="feng"]/@href')
- 需求:爬取boss的招聘信息
- 岗位名称
- 公司名称
- 薪资
- 岗位描述
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
'cookie':'lastCity=101010100; __c=1566877560; __g=-; Hm_lvt_194df3105ad7148dcf2b98a91b5e727a=1566877561; _uab_collina=156687756118178796315757; __l=l=%2Fwww.zhipin.com%2F&r=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DidbSvNzz2fLSl1WXiEmtINauVHUZYSNqejHny725pc5RTwaHqh5uDx1LewpyGmaT%26wd%3D%26eqid%3Dbadf667700040677000000025d64a772&friend_source=0&friend_source=0; __zp_stoken__=91d9QItKEtUk5dMMnDG7lwzq8mBW1g%2FkEsFOHXIi%2FwMd%2FPRRXc%2FPMKjsDYwsfC4b7vAT3FVnTmYBjGp8gW1OeZ5TdA%3D%3D; Hm_lpvt_194df3105ad7148dcf2b98a91b5e727a=1566879753; __a=69160831.1566877560..1566877560.16.1.16.16'
}
url = 'https://www.zhipin.com/job_detail/?query=python%E7%88%AC%E8%99%AB&city=101010100&industry=&position='
page_text = requests.get(url,headers=headers).text
#数据解析
tree = etree.HTML(page_text)
li_list = tree.xpath('//div[@class="job-list"]/ul/li')
for li in li_list:
# 需要将li表示的局部页面源码数据中的相关数据进行提取
# 如果xpath表达式被作用在了循环中,表达式要以./或者.//开头
detail_url = 'https://www.zhipin.com'+li.xpath('.//div[@class="info-primary"]/h3/a/@href')[0]
job_title = li.xpath('.//div[@class="info-primary"]/h3/a/div/text()')[0]
salary = li.xpath('.//div[@class="info-primary"]/h3/a/span/text()')[0]
company = li.xpath('.//div[@class="info-company"]/div/h3/a/text()')[0]
#对详情页的url发请求解析出岗位职责
detail_page_text = requests.get(detail_url,headers=headers).text
tree = etree.HTML(detail_page_text)
job_desc = tree.xpath('//div[@class="text"]//text()')
job_desc = ''.join(job_desc)
print(job_title,salary,company,job_desc)
- 另一种较为通用的xpath表达式的使用形式:管道符在xpath表达式中的应用,用于解析不规则布局的页面数据
- 爬取糗事百科的段子内容和作者名称
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
}
url = 'https://www.qiushibaike.com/text/page/4/'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
div_list = tree.xpath('//div[@id="content-left"]/div')
for div in div_list:
author = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()')[0]
content = div.xpath('.//div[@class="content"]/span//text()')
content = ''.join(content)
print(author,content)
中文乱码处理的问题
#指定一个通用的url模板
url = 'http://pic.netbian.com/4kmeishi/index_%d.html'
for page in range(1,3):
if page == 1:
new_url = 'http://pic.netbian.com/4kmeishi/'
else:
new_url = format(url%page)
response = requests.get(new_url,headers=headers)
#response.encoding = 'utf-8'
page_text = response.text
tree = etree.HTML(page_text)
li_list = tree.xpath('//*[@id="main"]/div[3]/ul/li')
for li in li_list:
img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0]
img_name = li.xpath('./a/b/text()')[0]
img_name = img_name.encode('iso-8859-1').decode('gbk')
回顾
- 简历模板:
- ConectionPool:
- 原因:
- 1.在短时间内向网站发起了一个高频的请求
- 使用代理
- 2.连接池(http)中的资源被耗尽
- 立即将请求断开:
- Connection:close
- 立即将请求断开:
- 1.在短时间内向网站发起了一个高频的请求
- 原因:
- ConectionPool:
-
高清图片:
- 图片懒加载:在img标签中应用了伪属性
-
回顾
- 数据解析的作用:为了实现聚焦爬虫
- bs4:
- soup.tagName
- find/find_all('tagName',attrName='value')
- select('Selector')
-
空格
-
- string/text
- tag['href']
- xpath:
- //tagName
- //tagName[@attrName="value"]
- //div[1]
- //text or /text
- //a/@href
- bs4和xpath最明显的一个区别什么?
- 解析出携带标签的局部内容?
- bs4相关标签定位的方法或者属性返回值就是携带标签的内容
- 代理
- cookie
- 验证码的识别
- 模拟登陆
- 代理
- 代理服务器:实现请求转发,从而可以实现更换请求的ip地址
- 在requests中如何将请求的ip进行更换
- 代理的匿名度:
- 透明:服务器知道你使用了代理并且知道你的真实ip
- 匿名:服务器知道你使用了代理,但是不知道你的真实ip
- 高匿:服务器不知道你使用了代理,更不知道你的真实ip
- 代理的类型:
- http:该类型的代理只可以转发http协议的请求
- https:只可以转发https协议的请求
- 免费代理ip的网站
- 快代理
- 西祠代理
- goubanjia
- 代理精灵(推荐):http://http.zhiliandaili.cn/
- 在爬虫中遇到ip被禁掉如何处理?
- 使用代理
- 构建一个代理池
- 拨号服务器
import requests
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36'
}
url = 'https://www.baidu.com/s?wd=ip'
#proxies={'http/https':'ip:port'}
page_text = requests.get(url=url,headers=headers,proxies={'https':'1.197.203.187:9999'}).text
with open('ip.html','w',encoding='utf-8') as fp:
fp.write(page_text)
#基于代理精灵构建一个ip池
from lxml import etree
all_ips = [] #列表形式的代理池
proxy_url = 'http://t.11jsq.com/index.php/api/entry?method=proxyServer.generate_api_url&packid=1&fa=0&fetch_key=&groupid=0&qty=52&time=1&pro=&city=&port=1&format=html&ss=5&css=&dt=1&specialTxt=3&specialJson=&usertype=2'
proxy_page_text = requests.get(url=proxy_url,headers=headers).text
tree = etree.HTML(proxy_page_text)
proxy_list = tree.xpath('//body//text()')
for ip in proxy_list:
dic = {'https':ip}
all_ips.append(dic)
all_ips
import random
#爬取西祠代理中的免费代理ip
url = 'https://www.xicidaili.com/nn/%d'
free_proxies = []
for page in range(1,30):
new_url = format(url%page)
page_text = requests.get(new_url,headers=headers,proxies=random.choice(all_ips)).text
tree = etree.HTML(page_text)
tr_list = tree.xpath('//*[@id="ip_list"]//tr')[1:]#xpath表达式中不可以出现tbody
for tr in tr_list:
ip = tr.xpath('./td[2]/text()')[0]
port = tr.xpath('./td[3]/text()')[0]
t_type = tr.xpath('./td[7]/text()')[0]
dic = {
'ip':ip,
'port':port,
'type':t_type
}
free_proxies.append(dic)
print('第{}页爬取完毕!!!'.format(page))
print(len(free_proxies))
Cookie
- 作用:保存客户端的相关状态
- 爬取雪球网中的新闻资讯数据:https://xueqiu.com/
- 在请求中携带cookie,在爬虫中如果遇到了cookie的反爬如何处理?
- 手动处理
- 在抓包工具中捕获cookie,将其封装在headers中
- 应用场景:cookie没有有效时长且不是动态变化
- 自动处理
- 使用session机制
- 使用场景:动态变化的cookie
- session对象:该对象和requests模块用法几乎一致.如果在请求的过程中产生了cookie,如果该请求使用session发起的,则cookie会被自动存储到session中.
- 手动处理
#获取一个session对象
session = requests.Session()
main_url = 'https://xueqiu.com' #推测对该url发起请求会产生cookie
session.get(main_url,headers=headers)
url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json'
params = {
'since_id': '-1',
'max_id': '20346152',
'count': '15',
'category': '-1',
}
page_text = session.get(url,headers=headers,params=params).json()
page_text
验证码识别
- 相关的线上打码平台识别
- 打码兔
- 云打码
- 超级鹰:http://www.chaojiying.com/about.html
- 1.注册,登录(用户中心的身份认证)
- 2.登录后:
- 创建一个软件:软件ID->生成一个软件id
- 下载示例代码:开发文档->python->下载
- 平台实例代码的演示
#!/usr/bin/env python
# coding:utf-8
import requests
from hashlib import md5
class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.base_params = {
'user': self.username,
'pass2': self.password,
'softid': self.soft_id,
}
self.headers = {
'Connection': 'Keep-Alive',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
}
def PostPic(self, im, codetype):
"""
im: 图片字节
codetype: 题目类型 参考 http://www.chaojiying.com/price.html
"""
params = {
'codetype': codetype,
}
params.update(self.base_params)
files = {'userfile': ('ccc.jpg', im)}
r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
return r.json()
def ReportError(self, im_id):
"""
im_id:报错题目的图片ID
"""
params = {
'id': im_id,
}
params.update(self.base_params)
r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
return r.json()
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用户中心>>软件ID 生成一个替换 96001
im = open('a.jpg', 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
print(chaojiying.PostPic(im, 1004)['pic_str']) #1902 验证码类型 官方网站>>价格体系 3.4+版 print 后要加()
def getCodeImgText(imgPath,img_type):
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用户中心>>软件ID 生成一个替换 96001
im = open(imgPath, 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
return chaojiying.PostPic(im, img_type)['pic_str']
url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
img_code_data = requests.get(img_src,headers=headers).content
with open('./gushiwen.jpg','wb') as fp:
fp.write(img_code_data)
img_text = getCodeImgText('./gushiwen.jpg',1004)
print(img_text)
- 为什么在爬虫中需要实现模拟登录?
- 有的数据是必须经过登录后才可以显示出来的!
- 涉及到的反爬:
- 验证码
- 动态请求参数:每次请求对应的请求参数都是动态变化
- 动态捕获:通常情况下,动态的请求参数都会被隐藏在前台页面的源码中
- cookie
def getCodeImgText(imgPath,img_type):
chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用户中心>>软件ID 生成一个替换 96001
im = open(imgPath, 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
return chaojiying.PostPic(im, img_type)['pic_str']
#使用session捕获cookie
s = requests.Session()
first_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
s.get(first_url,headers=headers)
url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
page_text = requests.get(url,headers=headers).text
tree = etree.HTML(page_text)
img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
img_code_data = s.get(img_src,headers=headers).content
with open('./gushiwen.jpg','wb') as fp:
fp.write(img_code_data)
img_text = getCodeImgText('./gushiwen.jpg',1004)
print(img_text)
#动态捕获动态的请求参数
__VIEWSTATE = tree.xpath('//*[@id="__VIEWSTATE"]/@value')[0]
__VIEWSTATEGENERATOR = tree.xpath('//*[@id="__VIEWSTATEGENERATOR"]/@value')[0]
#点击登录按钮后发起请求的url:通过抓包工具捕获
login_url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx'
data = {
'__VIEWSTATE': __VIEWSTATE,
'__VIEWSTATEGENERATOR': __VIEWSTATEGENERATOR,
'from': 'http://so.gushiwen.org/user/collect.aspx',
'email': 'www.zhangbowudi@qq.com',
'pwd': 'bobo328410948',
'code': img_text,
'denglu': '登录',
}
main_page_text = s.post(login_url,headers=headers,data=data).text
with open('main.html','w',encoding='utf-8') as fp:
fp.write(main_page_text)
基于线程池的异步爬取
url = 'https://www.qiushibaike.com/text/page/%d/'
urls = []
for page in range(1,11):
new_url = format(url%page)
urls.append(new_url)
urls
def get_request(url): #必须有一个参数
return requests.get(url,headers=headers).text
from multiprocessing.dummy import Pool
pool = Pool(10)
response_text_list = pool.map(get_request,urls) #使用自定义的函数func异步的处理urls列表中的每一个列表元素
print(response_text_list)
浙公网安备 33010602011771号