学习概要:
模拟用户登录
一、云打码平台
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) # 将返回的每一个视频流,放入回调函数,将视频流保存成视频