爬虫:模拟登陆访问(验证码)+ selenium + 线程池
学习概要:

模拟用户登录
一、云打码平台
1、使用的是云打码平台来解析验证码
用户登录,除了用户名和密码之外,还需要发送验证码才能够成功,但是验证码是图片,需要获取到里面的数字,然后发送给后端。
因此,这里采用的是 云打码 平台,来获取验证码图片中的内容,返回内容
- 相关的门户网站在进行登录的时候,如果用户连续登录的次数超过3次或者5次的时候,就会在登录页中动态生成验证码。通过验证码达到分流和反爬的效果。
2、云打码平台
- 1.对携带验证码的页面数据进行抓取 - 2.可以将页面数据中验证码进行解析,验证码图片下载到本地 - 3.可以将验证码图片提交给三方平台进行识别,返回验证码图片上的数据值 - 云打码平台: - 1.在官网中进行注册(普通用户和开发者用户) - 2.登录开发者用户: - 1.实例代码的下载(开发文档-》调用实例及最新的DLL-》PythonHTTP实例下载) - 2.创建一个软件:我的软件-》添加新的软件 -3.使用示例代码中的源码文件中的代码进行修改,让其识别验证码图片中的数据值
3、验证码,登陆成功,涉及到了session
可以使用requests.Session()
使用session发送请求,可以获取到cookie
4、封装的类
# yundama.py
''' ### 云打码平台 - 注册:普通用户和开发者用户 - 登录: - 登录普通用户(查看余额) - 登录开发者用户: - 创建一个软件:我的软件->创建软件 - 下载示例代码:开发者中心->下载最新的DLL->pythonHttp示例代码下载 ''' import http.client, mimetypes, urllib, json, time, requests ###################################################################### class YDMHttp: apiurl = 'http://api.yundama.com/api.php' username = '' password = '' appid = '' appkey = '' def __init__(self, username, password, appid, appkey): self.username = username self.password = password self.appid = str(appid) self.appkey = appkey def request(self, fields, files=[]): response = self.post_url(self.apiurl, fields, files) response = json.loads(response) return response def balance(self): data = {'method': 'balance', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey} response = self.request(data) if (response): if (response['ret'] and response['ret'] < 0): return response['ret'] else: return response['balance'] else: return -9001 def login(self): data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey} response = self.request(data) if (response): if (response['ret'] and response['ret'] < 0): return response['ret'] else: return response['uid'] else: return -9001 def upload(self, filename, codetype, timeout): data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'codetype': str(codetype), 'timeout': str(timeout)} file = {'file': filename} response = self.request(data, file) if (response): if (response['ret'] and response['ret'] < 0): return response['ret'] else: return response['cid'] else: return -9001 def result(self, cid): data = {'method': 'result', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid)} response = self.request(data) return response and response['text'] or '' def decode(self, filename, codetype, timeout): cid = self.upload(filename, codetype, timeout) if (cid > 0): for i in range(0, timeout): result = self.result(cid) if (result != ''): return cid, result else: time.sleep(1) return -3003, '' else: return cid, '' def report(self, cid): data = {'method': 'report', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid), 'flag': '0'} response = self.request(data) if (response): return response['ret'] else: return -9001 def post_url(self, url, fields, files=[]): for key in files: files[key] = open(files[key], 'rb') res = requests.post(url, files=files, data=fields) return res.text ###################################################################### def get_code(username,password,code_img,code_type): # 用户名(普通用户) username = username #输入普通的登录用户名和密码用于扣费,谁的都可以,不一定和开发者登录用户一致 # 密码 password = password #'bobo328410948' # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得! appid = 6003 # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得! appkey = '1f4b564483ae5c907a1d34f8e2f2776c' # 图片文件 filename = code_img #验证码的图片 # 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html codetype = code_type # 超时时间,秒 timeout = 30 # 检查 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))
二、人人网模拟登陆爬取登录成功之后的界面(requests.Session() , 发送请求可以携带cookie访问登录成功的界面)
# 人人网模拟登录,爬取登陆成功之后展示的网页内容 import requests from lxml import etree import urllib session = requests.Session() #获取验证码 ren_url = 'http://www.renren.com/' #获取登录界面的url headers = { "User-Agent":'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36' } ren_text = requests.get(url = ren_url,headers=headers).text tree = etree.HTML(ren_text) img_url = tree.xpath('//*[@id="verifyPic_login"]/@src')[0] #定位到验证码图片的img标签,获取验证码图片的链接,xpath获取的是列表所以要列表取值 urllib.request.urlretrieve(url=img_url,filename='./code_img.jpg') #将图片保存 code_data = get_code('bobo328410948','bobo328410948','code_img.jpg',2004) #识别验证码图片中的数据值,该方法需要导入, #模拟登录 url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2019141540720' #登录访问的界面,需要用fiddler抓包,data是从抓包工具中拷贝过来的携带的参数 data = { 'captcha_type':'web_login', 'domain':'renren.com', 'email':'chenyanhong0202@126.com', 'f':'http%3A%2F%2Fwww.renren.com%2F453427437', 'icode':'', 'key_id':'1', 'origURL':'http://www.renren.com/home', 'password':'4fb68112934e7004996da1a4e0676c27c8b898b7e5f7c26b4e19f696187dfe3c', 'rkey':'c5c08b36d1daef7b10b7ae3c886850e6' } session.post(url=url,headers=headers,data=data) #该次请求产生的cookie会被自动存储到session对象中, # 再次使用session发送请求时会携带cookie #携带cookie访问登录成功的界面 detail_url = 'http://www.renren.com/453427437' #登录成功之后展会的url detail_text = session.get(url=detail_url,headers=headers).text #此时发送get,请求就携带了session,所以可以登录成功 with open("./renren.html","w",encoding="utf-8")as f: f.write(detail_text)
备注:
1、一般的登录界面,登陆成功之后,会跳转页面,用浏览器抓包,会抓不到,因此需要采用 抓包工具 fildder 来抓取,可以获取到访问的 url 和携带的参数
2、在登录成功之后,后台会返回给浏览器一个 cookie 值,此时可以使用 requests.Session() 来发送请求,获取到的cookie会保存到session中,再次使用session发送其他的请求就会携带 cookie 了
三、模拟登录古诗文网(有些数据参数,如果是动态获得的,需要到前台页面html中获取)
#模拟登录古诗文网 https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx import requests import urllib from lxml import etree s = requests.Session() login_url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx' headers = { "User-Agent":'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36' } page_text = requests.get(url=login_url,headers=headers).text tree = etree.HTML(page_text) img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0] img_data = s.get(url=img_src,headers=headers).content #获取图片的时候使用session发送get请求,获取session with open('./img.jpg','wb') as fp: fp.write(img_data) img_text = get_code('bobo328410948','bobo328410948','./img.jpg',1004) #模拟登录 url = 'https://so.gushiwen.org/user/login.aspx?from=http%3a%2f%2fso.gushiwen.org%2fuser%2fcollect.aspx' data = { "__VIEWSTATE":"9AsGvh3Je/0pfxId7DYRUi258ayuEG4rrQ1Z3abBgLoDSOeAUatOZOrAIxudqiOauXpR9Zq+dmKJ28+AGjXYHaCZJTTtGgrEemBWI1ed7oS7kpB7Rm/4yma/+9Q=", "__VIEWSTATEGENERATOR":"C93BE1AE", "from":"http://so.gushiwen.org/user/collect.aspx", "email":"www.zhangbowudi@qq.com", "pwd":"bobo328410948", "code":img_text, "denglu":"登录", } page_text = s.post(url=url,headers=headers,data=data).text #发送登录请求,利用session发送获取cookie with open('./gushiwen.html','w',encoding='utf-8') as fp: fp.write(page_text)
总结:
1、上述的 __VIEWSTATE 字段,值的样式密文不确定的,一般为动态变化的,每次的请求都不一样,这种情况会在前台页面中有,解决办法:
在前台页面的html中,检查,直接搜索该字段,然后看看再哪个标签中,然后解析出来内容,再使用即可
selenium使用
一、selenium介绍
1、环境安装
- 环境安装:pip install selenium
- 编码流程:
- 导报:from selenium import webdriver
- 实例化某一款浏览器对象
- 自指定自动化操作代码
2、什么是selenium?
是Python的一个第三方库,对外提供的接口可以操作浏览器,然后让浏览器完成自动化的操作。
selenium 可以获取动态加载的数据的完整 html,使用selenium 就不需要使用requests模块了,page_source属性中包含了所有的html数据。
selenium 要和浏览器的驱动程序一起使用(chromedriver.exe)。
动态加载数据的两种形式:1.ajax发送请求返回数据,2.通过 js 来获取数据
3、环境搭建
安装selenum:pip install selenium
获取某一款浏览器的驱动程序(以谷歌浏览器为例)
谷歌浏览器驱动下载地址:http://chromedriver.storage.googleapis.com/index.html
下载的驱动程序必须和浏览器的版本统一,大家可以根据http://blog.csdn.net/huilan_same/article/details/51896672中提供的版本映射表进行对应
4、注意点:
使用 selenium 中的 webdriver ,浏览器可以实现自动化操作,开发者不再需要手动点击,拖动,滑动等操作;
这种方式获取的 html 页面中包含动态加载的数据,直接对获取的html进行解析即可,之前需要判断数据是否为动态加载,然后对其访问的url ,再次发送请求,进行解析。
5、使用的常用语法
#导包 from selenium import webdriver #创建浏览器对象,通过该对象可以操作浏览器 browser = webdriver.Chrome('驱动路径') #使用浏览器发起指定请求 browser.get(url) #使用下面的方法,查找指定的元素进行操作即可 find_element_by_id 根据id找节点 find_elements_by_name 根据name找 find_elements_by_xpath 根据xpath查找 find_elements_by_tag_name 根据标签名找 find_elements_by_class_name 根据class名字查找
二、浏览器自动化实现在百度中搜索“小狗”
from selenium import webdriver from time import sleep bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Desktop\爬虫+数据\day_03_爬虫\chromedriver.exe') #指定chromedriver bro.get(url='https://www.baidu.com/') #等于在地址栏输入url sleep(2) text_input = bro.find_element_by_id('kw') #获取标签 text_input.send_keys('小狗') #为标签输入内容 sleep(2) bro.find_element_by_id('su').click() #点击按钮 sleep(3) #获取当前的页面源码数据 print(bro.page_source) #page_source中的数据会包含动态加载的数据 bro.quit()
三、获取豆瓣电影中更多电影详情数据(实现屏幕自动滑动滚轮,获取到的数据包含动态加载的)
#获取豆瓣电影中更多电影详情数据
from selenium import webdriver
from time import sleep
url = 'https://movie.douban.com/typerank?type_name=%E6%83%8A%E6%82%9A&type=19&interval_id=100:90&action=' bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Desktop\爬虫+数据\day_03_爬虫\chromedriver.exe') bro.get(url) #等于在地址栏输入url sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') #滑动一屏的距离 sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(2) page_text = bro.page_source with open('./douban.html','w',encoding='utf-8') as fp: fp.write(page_text) sleep(1) bro.quit()
四、无屏实现浏览器的自动化(无屏)
浏览器的自动化操作,会可视化的呈现在我们面前,我们不希望展示给用户看,因此需要将屏幕隐藏掉,
方式一:使用 phantomJs(不常用)
使用的方法和 chromedriver.exe 的方式一样,配置参数即可,将chromedriver.exe 的路径换成 phantomjs-2.1.1-windows\bin\phantomjs.exe的路径
这种方式不常用,因为 phantomJs 已经不再更新维护了。
#phantomJs #获取豆瓣电影中更多电影详情数据
from selenium import webdriver
from time import sleep
url = 'https://movie.douban.com/typerank?type_name=%E6%83%8A%E6%82%9A&type=19&interval_id=100:90&action=' bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Desktop\爬虫+数据\day_03_爬虫\phantomjs-2.1.1-windows\bin\phantomjs.exe') #更换参数配置即可 bro.get(url) #等于在地址栏输入url sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(2) page_text = bro.page_source with open('./douban.html','w',encoding='utf-8') as fp: fp.write(page_text) sleep(1) bro.quit()
方式二:配置浏览器的参数(常用)
这里使用谷歌浏览器,配置参数,记住就好
#谷歌无头浏览器
from selenium import webdriver
from time import sleep
from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu') #获取豆瓣电影中更多电影详情数据 url = 'https://movie.douban.com/typerank?type_name=%E6%83%8A%E6%82%9A&type=19&interval_id=100:90&action=' bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Desktop\爬虫+数据\day_03_爬虫\chromedriver.exe',chrome_options=chrome_options) #这里需要多添加一个参数 bro.get(url) #等于在地址栏输入url sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(3) bro.execute_script('window.scrollTo(0,document.body.scrollHeight)') sleep(2) page_text = bro.page_source with open('./douban.html','w',encoding='utf-8') as fp: fp.write(page_text) print(page_text) sleep(1) bro.quit()
五、爬取QQ空间(有iframe标签的要先定位到iframe标签)
有些标签是隐藏在 ifreame 标签中的额,常规的方法获取不到,定位的标签也没有问题的话,就查看一下,是否包含在iframe中,
需要定位到 frame标签中,再去定位具体的标签
#qq空间
from selenium import webdriver
from time import sleep
bro = webdriver.Chrome(executable_path=r'C:\Users\Administrator\Desktop\爬虫+数据\day_03_爬虫\chromedriver.exe') url = 'https://qzone.qq.com/' bro.get(url=url) #等于在地址栏输入url sleep(2) #定位到一个具体的iframe bro.switch_to.frame('login_frame') # bro.find_element_by_id('switcher_plogin').click() #由于 id为seitcher_plogin 的标签,在iframe标签中,因此需要先定位到具体的frame, sleep(2) bro.find_element_by_id('u').send_keys('460086804') bro.find_element_by_id('p').send_keys('shuo0127') bro.find_element_by_id('login_button').click() sleep(5) page_text = bro.page_source with open('qq.html','w',encoding='utf-8') as fp: fp.write(page_text) bro.quit()
线程池
一、利用线程池爬取梨视频
耗时较多的地方,比如下载视频和保存视频的地方,使用线程池即可。
由于想要的数据url,是在js中,因此不能使用bs4或者xpath解析,只能使用正则来解析
# 爬取梨视频数据,获取热视频(这里有4个视频) import random import re from multiprocessing.dummy import Pool import requests from lxml import etree # 获取视频流 def getVideoData(url): return requests.get(url=url, headers=headers).content # 保存视频流 def saveVideo(data): fileName = str(random.randint(0, 5000)) + '.mp4' with open(fileName, 'wb') as fp: fp.write(data) # 实例化一个线程池对象 pool = Pool(5) # 线程池 url = 'https://www.pearvideo.com/category_1' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' } page_text = requests.get(url=url, headers=headers).text tree = etree.HTML(page_text) li_list = tree.xpath('//div[@id="listvideoList"]/ul/li') # 获取包含视频的li标签返回列表 # 获取视频的连接url放到列表中 video_url_list = [] for li in li_list: detail_url = 'https://www.pearvideo.com/' + li.xpath('./div/a/@href')[0] detail_page = requests.get(url=detail_url, headers=headers).text video_url = re.findall('srcUrl="(.*?)",vdoUrl', detail_page, re.S)[0] #由于想要的数据url,是在js中,因此不能使用bs4或者xpath解析,只能使用正则来解析 video_url_list.append(video_url) # 在线程池中使用map函数 video_data_list = pool.map(getVideoData, video_url_list) # 将列表中的而每一个元素url,放入回调函数中获取视频流二进制,返回列表形式 pool.map(saveVideo, video_data_list) # 将返回的每一个视频流,放入回调函数,将视频流保存成视频

浙公网安备 33010602011771号