模拟登陆
-
为什么要进行模拟登陆
- 有些时候,我们需要爬取一些基于个人用户的用户信息(需要登录后才可以查看的)
-
为什么需要识别验证码
- 因为验证码往往是作为登录请求中的请求参数被使用
-
验证码识别:借助于线上的一款打码平台(超级鹰,云打码,打码兔)
-
超级鹰的使用流程:http://www.chaojiying.com/about.html
- 注册:注册一个<用户中心>身份的账号
- 登录:基于<用户中心>进行登录
- 点击 软件ID -->生成一个软件id
- 下载示例代码:点击开发文档->选择python语言->点击下载
-
实现模拟登陆
- 就是使用抓包工具捕获登录按钮对应的post请求数据包,从该数据包中提取出请求的url和请求参数,就可以使用requests进行请求发送
-
cookie操作:
- 手动处理(不建议):
- 将cookie放置到headers中,灵活性差,cookie存储有时间上限
- 自动处理(建议):使用requests模块中的Session对象:
- Session对象:该对象可以像requests一样发送get和post请求,当该对象在进行请求发送的过程中,产生cookie,则该cookie会被自动存储到该对象中
- session = requests.Session()
- Session对象:该对象可以像requests一样发送get和post请求,当该对象在进行请求发送的过程中,产生cookie,则该cookie会被自动存储到该对象中
- 手动处理(不建议):
-
模拟登陆案例
#模拟登陆爬取古诗文网站登陆界面 import requests from lxml import etree from chaojiying_Python.chaojiying import Chaojiying_Client #当你处理登陆爬虫时要将请求换成session session = requests.Session() #将超级鹰下载的包封装成一个函数进行引入调用 def get_codeImg_text(imgPath, imgType): chaojiying = Chaojiying_Client('超级鹰账号', '超级鹰密码', ' 899991') #用户中心>>软件ID 生成一个替换 96001 im = open(imgPath, 'rb').read() #本地图片文件路径 来替换 a.jpg 有时WIN系统须要// # print(chaojiying.PostPic(im, 1902)) return chaojiying.PostPic(im, imgType)['pic_str'] headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36" } #模拟登录 #获取验证码图片 url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx' #解析验证码图片 page_text = requests.get(url=url,headers=headers).text tree = etree.HTML(page_text) code_img_src = 'https://so.gushiwen.org/'+tree.xpath('//*[@id="imgCode"]/@src')[0] code_img_data = session.get(url=code_img_src,headers=headers).content with open('./code.jpg','wb') as fp: fp.write(code_img_data) #解析动态参数 __VIEWSTATE = tree.xpath('//input[@id="__VIEWSTATE"]/@value')[0] __VIEWSTATEGENERATOR = tree.xpath('//input[@id="__VIEWSTATEGENERATOR"]/@value')[0] print(__VIEWSTATE,__VIEWSTATEGENERATOR) #使用打码平台识别出来的验证码图片数据 codeImg_text = get_codeImg_text('./code.jpg',1902) print(codeImg_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": "你的账号", "pwd": "你的密码", "code": codeImg_text, "denglu": "登录", } #登陆发送post请求,且传送data pages_text = session.post(url=login_url,headers=headers,data=data).text with open('./gushiwen.html','w',encoding='utf-8') as fp: fp.write(pages_text)
代理IP
-
在爬取数据的时候可能会报出一个HttpConnectilonPool的错误
- 原因
- 请求池资源被耗尽,在headers中加入一个键值对:'Connection':'close'
- 请求ip被服务器禁用
- 可以使用代理IP解决
- 原因
-
代理:
- 概念:代理服务器
- 代理IP:代理服务器的ip
- 代理IP网站:快代理、西祠代理、goubanjia
- 代理方式及参数
- get/post(url,headers,data/params,proxies={'http':'ip:port'})
-
什么是高匿名、匿名和透明代理?他们有什么区别?
- 使用透明代理,对方服务器可以知道你使用了代理,并且也知道你的真实IP
- 使用匿名代理.对方服务器可以知道你使用了代理,但不知道你的真实IP
- 使用高匿名代理:对方服务器不知道你使用了代理,更不知道你的真实IP
-
类型:
- http:只可以发起http请求
- https:只可以发起https请求
-
简单的ip代理演示
url = 'https://www.baidu.com/s?wd=ip' page_text = requests.get(url=url,headers=headers,proxies={'http':'177.91.254.51:9999'}) with open('./ip.html','w',encoding='utf-8') as fp fp.wirte(page_text)
-
代理IP被封如何解决?
线程池
-
线程池:尽可能用在耗时较为严重的操作中
-
视频的请求下载
-
视频的持久化存储
-
使用模块
#模块引入 from multiprocessing.dummy import Pool #创建线程数量 pool = Pool(4) pool.map(func,iterable,chunksize=None) #map方法可以基于异步实现:让参数1对应的函数对参数2对应的容器元素一次进行操作,参数二必须是可迭代的,比如最典型的列表
-
-
模拟请求阻塞操作
- 普通的访问方式
普通的访问方式 import time def my_request(url): print('正在请求:',url) #设置了休眠等待 time.sleep(10) print('请求完毕:', url) urls = [ 'www.1.com', 'www.2.com', 'www.3.com', 'www.4.com', ] start = time.time() for url in urls: my_request(url) print(time.time()-start)
-
使用线程池
#使用线程池 import time from multiprocessing.dummy import Pool pool = Pool(4) def my_request(url): print('正在请求:',url) time.sleep(10) print('请求完毕:', url) urls = [ 'www.1.com', 'www.2.com', 'www.3.com', 'www.4.com', ] start = time.time() pool.map(my_request,urls) print(time.time()-start) pool.close() #join的意思是让主线程等待子线程全部结束后再结束 pool.join()
-
案例:对梨视频的视频爬取
#对梨视频的视频爬取 import re,random import requests from lxml import etree from multiprocessing.dummy import Pool pool = Pool(4) #定义一个函数,传送每一个url,下载视频 def downloadDate(url): #content爬取二进制使用 return requests.get(url=url,headers=headers).content #定义一个函数,存储每一个视频 def saveDate(data): name = str(random.randint(0,10000))+'mp4' with open(name,'wb') as fp: fp.write(data) print(name,'下载成功') url = 'https://www.pearvideo.com/category_1' headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36" } page_text = requests.get(url=url,headers=headers).text tree = etree.HTML(page_text) li_list = tree.xpath('//ul[@id="listvideoListUl"]/li') #定义一个空列表存放所有的视频链接 urls = [] for li in li_list: detail_url = 'https://www.pearvideo.com/'+li.xpath('./div/a/@href')[0] pages_text = requests.get(url=detail_url,headers=headers).text ex = 'srcUrl="(.*?)",vdoUrl' video_url = re.findall(ex,pages_text,re.S)[0] urls.append(video_url) #在耗时较为严重的地方使用线程池,视频的请求下载及持久化存储 #让函数对urls中的每一个元素进行异步网络请求下载 #函数不能传参,不能加括号 #datas是一个列表,存储的是所有的视频二进制的数据 datas = pool.map(downloadDate,urls) pool.map(saveDate,datas) pool.close() #join的意思是让主线程等待子线程全部结束后再结束 pool.join() #在获取视频链接的时候发现视频并不是存储在标签中,而是在js中的变量里, # 此时我们只能使用支持各种匹配的正则来获取链接 """ var contId="1565375",liveStatusUrl="liveStatus.jsp", liveSta="",playSta="1",autoPlay=!1,isLiving=!1,isVrVideo=!1, hdflvUrl="",sdflvUrl="",hdUrl="",sdUrl="",ldUrl="", srcUrl="https://video.pearvideo.com/mp4/short/20190612/cont-1565375-14010027-hd.mp4", vdoUrl=srcUrl,skinRes="//www.pearvideo.com/domain/skin",videoCDN="//video.pearvideo.com"; """