模拟登陆
-
为什么要进行模拟登陆
- 有些时候,我们需要爬取一些基于个人用户的用户信息(需要登录后才可以查看的)
-
为什么需要识别验证码
- 因为验证码往往是作为登录请求中的请求参数被使用
-
验证码识别:借助于线上的一款打码平台(超级鹰,云打码,打码兔)
-
超级鹰的使用流程: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"; """