爬虫:模拟登陆访问(验证码)+ 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))
View Code

二、人人网模拟登陆爬取登录成功之后的界面(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)  # 将返回的每一个视频流,放入回调函数,将视频流保存成视频

 

posted @ 2019-02-28 15:22  葡萄想柠檬  Views(1273)  Comments(1)    收藏  举报
目录代码