- 代理
- cookie
- 模拟登陆
- 验证码的识别
- 线程池
- 单线程+多任务异步协程
出现HTTPConnectionPool错误的原因以及解决办法:
1.连接池资源被耗尽(请求太多又没有关闭连接)
解决办法: 在headers中加入 'Connection':'close'
2.ip被封
一.代理服务器
- 代理:代理服务器
- 基于代理的网站:
- 站大爷
- goubanjia
- 快代理
- 西祠代理
- 代理的匿名度:
- 透明:使用透明代理,对方服务器可以知道你使用了代理,并且也知道你的真实IP
- 匿名:对方服务器可以知道你使用了代理,但不知道你的真实IP。
- 高匿:对方服务器不知道你使用了代理,更不知道你的真实IP
- 类型:
- http:代理服务器只可以转发http协议的请求
- https:代理服务器只可以转发https协议的请求
- 编码:
- 在get或者post方法中应用一个proxies的参数,给其参数赋值为{'http':'ip:port'}
简易版代理应用:
#简单版
import requests
#简易的代理池
proxies_list = [
{'http':'119.190.193.7:8060'},
{'http':'119.190.193.7:8060'},
{'http':'119.190.193.7:8060'},
]
url = 'https://www.baidu.com/s?wd=ip'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'
}
page_text = requests.get(url,headers=headers,proxies={'https':'58.218.200.223:30720'}).text
with open('ip.html','w',encoding='utf-8') as fp:
fp.write(page_text)
升级版代理:
#升级版(减少被封的几率) import requests import random https = [ {'https':'223.19.212.30:8380'}, {'https':'221.19.212.30:8380'} ] http = [ {'http':'223.19.212.30:8380'}, {'http':'221.19.212.30:8380'} ] headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36' } url = 'https://www.baidu.com/s?wd=ip' if url.split(':')[0] == 'https': page_text = requests.get(url=url,headers=headers,proxies=random.choice(https)).text else: page_text = requests.get(url=url,headers=headers,proxies=random.choice(http)).text with open('./ip.html','w',encoding='utf-8') as fp: fp.write(page_text)
cookie处理
- 处理方式:
- 手动处理:将cookie的键值对手动添加到headers字典中,然后将headers作用到get或者post方法的headers参数中
- 自动处理:使用Session。
- session作用:session可以和requests一样进行请求的发送
- 特性:使用session发起请求,如果请求过程中产生cookie,则cookie会被自动存储到session中
url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=-1&count=10&category=-1' #创建一个session对象 session = requests.Session() #获取cookie session.get('https://xueqiu.com/',headers=headers) #携带cookie进行的请求发送 session.get(url,headers=headers).json()
模拟登陆
- 什么是模拟登陆
- 使用requests对登陆按钮的请求进行发送
- 为什么要模拟登陆
- 有的页面必须登陆之后才显示
- 验证码的识别
- 线上的打码平台:超级鹰,云打码,打码兔......
- 超级鹰:http://www.chaojiying.com/
- 超级鹰的使用流程
- 注册:用户中心身份的账户
- 登陆:
- 查看提分的剩余
- 创建一个软件ID:软件ID-》生成一个软件ID(ID的名称和说明)
- 下载示例代码:开发文档-》选择语言-》点击下载
以下代码是下载的文件中的,内容用的时候需加载:
#!/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() if __name__ == '__main__': chaojiying = Chaojiying_Client('超级鹰用户名', '超级鹰用户名的密码', '96001') #用户中心>>软件ID 生成一个替换 96001 im = open('a.jpg', 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要// print chaojiying.PostPic(im, 1902) #1902 验证码类型 官方网站>>价格体系 3.4+版 print 后要加()
识别古诗文中的验证码:
#识别古诗文网中的验证码图片
import requests from lxml import etree 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) code_img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0] img_data = requests.get(code_img_src,headers=headers).content with open('./code.jpg','wb') as fp: fp.write(img_data) #使用线上平台识别验证码 chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用户中心>>软件ID 生成一个替换 96001 im = open('code.jpg', 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要// print(chaojiying.PostPic(im, 1902)['pic_str'])
基于古诗文网的模拟登陆:
#识别古诗文网中的验证码图片 s = requests.Session() url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx' page_text = s.get(url,headers=headers).text tree = etree.HTML(page_text) code_img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0] #使用session对验证码图片发请求(会产生cookie) img_data = s.get(code_img_src,headers=headers).content with open('./code.jpg','wb') as fp: fp.write(img_data) #解析出动态参数的数据值 __VIEWSTATE = tree.xpath('//input[@id="__VIEWSTATE"]/@value')[0] __VIEWSTATEGENERATOR = tree.xpath('//input[@id="__VIEWSTATEGENERATOR"]/@value')[0] #使用线上平台识别验证码 chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用户中心>>软件ID 生成一个替换 96001 im = open('code.jpg', 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要// #验证码图片的文本数据 code_img_text = chaojiying.PostPic(im, 1902)['pic_str'] print(code_img_text) #模拟登陆 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": "[email protected]", "pwd": "bobo328410948", "code": code_img_text, "denglu": "登录", } login_page_text = s.post(login_url,headers=headers,data=data).text with open('gushiwen.html','w',encoding='utf-8') as fp: fp.write(login_page_text)
线程池的应用
- 异步操作可以和非异步操作结合使用
- 线程池最好只被应用在较为耗时的操作中
import time def request(url): print('正在请求:',url) time.sleep(2) print('请求成功:',url) start = time.time() urls = [ 'www.1.com', 'www.2.com', 'www.3.com', 'www.4.com', ] for url in urls: request(url) print(time.time()-start)
打印结果:
正在请求: www.1.com 请求成功: www.1.com 正在请求: www.2.com 请求成功: www.2.com 正在请求: www.3.com 请求成功: www.3.com 正在请求: www.4.com 请求成功: www.4.com 8.001457452774048
基于异步:
#基于异步操作 from multiprocessing.dummy import Pool start = time.time() pool = Pool(4) def request(url): print('正在请求:',url) time.sleep(2) print('请求成功:',url) urls = [ 'www.1.com', 'www.2.com', 'www.3.com', 'www.4.com', ] pool.map(request,urls) pool.close() pool.join() print(time.time()-start)
打印结果:
正在请求:正在请求: www.2.com 正在请求: www.3.com 正在请求: www.1.com www.4.com 请求成功: www.2.com 请求成功: www.3.com 请求成功:请求成功: www.4.com www.1.com 2.1071205139160156
爬取梨视频的短视频数据
#爬取梨视频的短视频数据 #var contId="1570697",liveStatusUrl="liveStatus.jsp",liveSta="", # playSta="1",autoPlay=!1,isLiving=!1,isVrVideo=!1,hdflvUrl="",sdflvUrl="",hdUrl="",sdUrl="",ldUrl="", # srcUrl="https://video.pearvideo.com/mp4/adshort/20190626/cont-1570697-14061078_adpkg-ad_hd.mp4",vdoUrl=srcUrl,skinRes="//www.pearvideo.com/domain/skin",videoCDN="//video.pearvideo.com"; import requests from lxml import etree import re pool = Pool(4) url = 'https://www.pearvideo.com/category_1' page_text = requests.get(url,headers=headers).text tree = etree.HTML(page_text) li_list = tree.xpath('//*[@id="listvideoListUl"]/li') all_video_urls = [] for li in li_list: detail_url = 'https://www.pearvideo.com/'+li.xpath('./div/a/@href')[0] detail_page_text = requests.get(detail_url,headers=headers).text #解析出视频的url ex = 'srcUrl="(.*?)",vdoUrl' video_url = re.findall(ex,detail_page_text,re.S)[0] all_video_urls.append(video_url) def dowmload_save(url): video_data = requests.get(url,headers=headers).content fileName = url.split('/')[-1] with open(fileName,'wb') as fp: fp.write(video_data) print(fileName,'下载成功') #视频数据的请求和持久化存储是比较耗时的,可以基于线程池来实现 pool.map(dowmload_save,all_video_urls)#参数1对应的函数必须只可以有一个参数
爬虫中的异步操作
- 线程池(适当)
- 单线程+多任务异步协程(推荐)
- 协程:coroutine
- 任务对象:stask
- 事件循环对象:event_loop