zoukankan      html  css  js  c++  java
  • 爬虫之requests

    爬虫概述

    • 什么是爬虫?

      • 通过编写程序让其模拟浏览器上网,然后去互联网中抓取数据的过程

    • 爬虫的分类

      • 通用爬虫:就是抓取一整张页面源码内容。

      • 聚焦爬虫:抓取的是页面中局部的内容

      • 增量式爬虫:可以监测网站数据更新的情况。抓取网站中最新更新出来的数据。

    • 反爬机制:对应的载体数网站。

    • 反反爬策略:对应的载体爬虫程序。

    • 探究一下爬虫的合法性:

      • 爬虫本身是不被法律禁止(中立性)

      • 爬取数据的违法风险的体现:

        • 爬虫干扰了被访问网站的正常运营

        • 爬虫抓取了受到法律保护的特定类型的数据或信息。

      • 如何规避违法的风险?

        • 严格遵守网站设置的robots协议;

        • 在规避反爬虫措施的同时,需要优化自己的代码,避免干扰被访问网站的正常运行;

        • 在使用、传播抓取到的信息时,应审查所抓取的内容,如发现属于用户的个人信息、隐私或者他人的商业秘密的,应及时停止并删除。

    • 第一个反爬机制:robots协议

      • 特性:防君子不防小人

    https和http相关

    • http协议:客户端和服务器端进行数据交互的形式。

    • 常用的请求头信息

      • User-Agent:请求载体的身份标识

      • Connection:close

    • 响应头信息

      • content-type

    • https:安全的http(加密)

      • 对称秘钥加密

      • 非对称秘钥加密

      • 证书秘钥加密(***):证书

    requests模块

    • 作用:模拟浏览器发请求。

    • 编码流程:

      • 指定url

      • 发起请求,获取响应对象

      • 获取响应数据

      • 持久化存储

    通用爬虫小练习

    1.简易的网页采集器

    import requests
    url = 'https://www.sogou.com/web'
    # 动态的参数
    wd = input('enter a key word:')
    param = { 'query':wd}
    # 携带了动态参数进行的请求发送
    response = requests.get(url=url,params=param)
    page_text = response.text
    fileName = wd+'.html'
    with open(fileName,'w',encoding='utf-8') as fp:
       fp.write(page_text)
    print(fileName,'下载成功!')

    上述程序执行后:

    • 乱码:修改响应数据的编码

    • 数据丢失:UA检测反爬机制

    • UA检测:网站会检测当前请求的请求载体的身份标识

    • UA伪装:

      • 需要将User-Agent对应的数据封装到字典种,将字典作用的请求方法的headers参数中

    # 简易的网页采集器
    url = 'https://www.sogou.com/web'
    # 动态参数
    msg = input('enter a key word:')
    params = { 'query':msg}
    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",}
    # 携带了动态参数进行的请求发送
    reponse = requests.get(url=url, params=params,headers=headers)
    #可以手动修改响应数据的编码
    response.encoding = 'utf-8'
    page_text = reponse.text
    filename = msg+'.html'
    with open(filename,'w',encoding='utf8') as f:
        f.write(page_text)

    2. 爬取豆瓣电影中电影详情数据

    import requests
    url = 'https://movie.douban.com/j/chart/top_list'
    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",
    }
    start = input('起始爱情电影:')
    limit = input('TOP爱情电影:')
    params = {
        "type":"13",
        "interval_id": "100:90",
        "action": "" ,
        "start": start,
        "limit":limit,
    }
    page_text = requests.get(url=url,params=params,headers=headers).json()
    for dic in page_text:
        title,score = dic['title'],dic['score']
        print('电影名:{},评分:{}'.format(title,score))

    动态加载的数据

    • 需要借助于抓包工具进行分析和动态加载数据对应url的提取

    3. 爬取肯德基餐厅的位置信息

    url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
    city = input('请输入要查询城市的名称:')
    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",
    }
    for i in range(1,8):
        data = {
            "cname":"", 
            "pid": "",
            "keyword": city,
            "pageIndex": i,
            "pageSize": "10",
        }
        page_json = requests.post(url=url,headers=headers,data=data).json()
    #     print(page_json)
        for dic in page_json['Table1']:
            rownum = dic['rownum']
            storeName = dic['storeName']
            addressDetail = dic['addressDetail']
            print('{}-{}-{}'.format(rownum,storeName,addressDetail))

    4. 化妆品公司详情爬取

    http://125.35.6.84:81/xk/
    
    # 1.检测页面中的数据是否为动态加载
    # 2.通过抓包工具找到动态加载数据对应的数据包,数据包中提取该请求的url
    # 3.发现首页中动态加载的数据包中并没有详情页的url,但是有每一家企业的id
    # 4.通过观察发现,每一家企业详情页的域名是一致的只有携带的id参数不同
    # 5.可以通过相同的域名结合着不同企业的id组成一个企业详情页的url
    # 6.通过抓包工具发现详情页中的企业详情数据是动态加载出来的
    # 7.在抓包工具中通过全局搜索找到了动态加载数据对应的数据包,并且可以提取url
    # 8.多家企业对应的详情数据对应的数据包的url都是一样的,只有携带的参数id的值不同
    url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsList'
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'
    }
    for page in range(1,11):
        data = {
            "on": "true",
            "page": str(page),
            "pageSize": "15",
            "productName": "",
            "conditionType": "1",
            "applyname": "",
            "applysn": "",
        }
        json_data = requests.post(url=url,data=data,headers=headers).json()
        print('第{}页爬取结束!'.format(page))
        for dic in json_data['list']:
            _id = dic.get('ID')
            #对企业详情数据对应的url发起一个post请求
            post_url = 'http://125.35.6.84:81/xk/itownet/portalAction.do?method=getXkzsById'
            data = {'id':_id}
            detail_data = requests.post(url=post_url,headers=headers,data=data).json()
            company_name = detail_data['epsName']
            print(company_name)

    5.爬取喜马拉雅的免费音频

    import requests
    url = 'https://www.ximalaya.com/revision/play/album?'
    headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'}
    for num in range(1,10):
        params = {
            "albumId": "9742789",
            "pageNum": num,
            "sort": "0",
            "pageSize": "30",
        }
        json_data = requests.get(url=url,headers=headers,params=params).json()
        for dic in json_data.get('data').get('tracksAudioPlay'):
            trackName = dic['trackName']
            trackUrl = dic['trackUrl']
            src = dic['src']
            url = 'https://www.ximalaya.com{}'.format(trackUrl)
            print(url)
            xs_data = requests.get(url=src,headers=headers).content
        #     print(xs_data)
            with open('./{}.mp3'.format(trackName),'wb') as fp:
                fp.write(xs_data)

    聚焦爬虫之数据解析

    • 实现数据解析的方式:

      • 正则

      • bs4

      • xpath

      • pyquery

    • 为什么要使用数据解析?

      • 数据解析是实现聚焦爬虫的核心技术,就是在一张页面源码中提取出指定的文本内容。

    • 数据解析的通用解析原理?

      • 我们要解析提取的数据都是存储在标签中间或者是标签的属性中

      • 1.标签定位

      • 2.取文本或者取

    一、正则解析

    1. 案例 爬糗事百科糗图

    // 通过检查源码发现目标div标签
    <div class="thumb"><a href="/article/121960143" target="_blank">
    <img src="//pic.qiushibaike.com/system/pictures/12196/121960143/medium/5E2GWJ5SP18QQ051.jpg" alt="重要通知">
    </a></div>
    import re
    import requests
    import os
    from urllib import request
    # 通用的url模板不可变的
    url = 'https://www.qiushibaike.com/pic/page/%d/?s=5205111'
    if not os.path.exists('./qiutuPic'):
        os.mkdir('./qiutuPic')
    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"}
    for page in range(1,36):
        # 新的url地址
        new_url = format(url%page)
        # 重新发送请求
        page_text = requests.get(url=new_url,headers=headers).text
        # 数据解析:img的src的属性值(图片的连接)
        ex = '<div class="thumb">.*?<img src="(.*?)" alt=.*?</div>'
        img_src_list = re.findall(ex,page_text,re.S) # 在这里使用正则的时候,必须有re.S参数
        for src in img_src_list:
            src = 'https:'+src
            img_name = src.split('/')[-1]
            img_path = './qiutuPic/'+img_name
            request.urlretrieve(src,img_path)
            print(img_name+'下载成功!')

    二、bs4 解析

    • 解析原理:

      • 1.实例化一个BeautifulSoup的一个对象,且将即将被解析的页面源码加载到该对象中

      • 2.需要调用bs对象中相关属性和方法进行标签定位和数据的获取

    • 环境安装

      • pip install lxml(解析器)

      • pip install bs4

    • BeautifulSoup对象的实例化

      • BeautifulSoup('fp','lxml'):将本地存储的一张HTML页面中的页面源码加载到bs4对象中 fp文件句柄

      • BeautifulSoup(page_text,'lxml'):将互联网请求到的页面源码数据加载到bs4对象

    bs相关属性和方法

    • soup.tagName:可以定位到第一次出现的tagName标签,返回值是一个单数

    • find('tagName') == soup.taagName

    • 属性定位:find('tagName',attrName='value'),返回的也是单数

    • find_all():和find的用法一样,只是返回值是一个列表

    • select('选择器'):id,class,标签,层级选择器,返回值为列表.>表示一个层级 空格表示多个层级

    • 取文本:string定位的是直系的文本内容,text,get_text()定位的是所有文本内容 两个获得东西是一样的

    • 取属性:tag['attrName']

    二、bs4案例

    1. 基础

    from bs4 import BeautifulSoup
    #使用bs解析本地存储的一张页面中相关的局部数据
    fp = open('./test.html','r',encoding='utf-8')
    soup = BeautifulSoup(fp,'lxml')
    #标签的定位
    soup.a
    soup.find('div',class_='tang')
    soup.find_all('div')
    soup.select('.song')
    soup.select('.tang > ul > li > a')
    soup.select('.tang > ul > li > a')[1].string
    soup.find('div',class_='song').text
    soup.find('div',class_='song').get_text()
    soup.select('.song > img')[0]['src']

    2. 爬取小说案例

    • 爬取诗词名句网中的西游记小说

      from bs4 import BeautifulSoup
      import requests
      url = 'http://www.shicimingju.com/book/xiyouji.html'
      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,headers=headers).text
      # 数据解析
      soup = BeautifulSoup(page_text,'lxml')
      a_list = soup.select('.book-mulu > ul > li > a') # 或者可以用 ./book-mulu li > a
      fp = open('./西游记.edub','w',encoding='utf-8')
      for a in a_list:
          title = a.string
          detail_url = 'http://www.shicimingju.com'+a['href']
          detail_page_text = requests.get(url=detail_url,headers=headers).text
          # 解析详情页的页面源码
          soup = BeautifulSoup(detail_page_text,'lxml')
          content = soup.find('div',class_='chapter_content').text
          fp.write(title+':'+content+'
      ')
          print(title,'下载成功!!')
      fp.close()

    三、xpath解析

    1.介绍

    • 优点:通用性强

    • 解析原理:

      • 1.实例化一个etree的对象,将即将被解析的页面加载到该对象中

      • 2.调用etree对象中的xpath方法结合着不同的xpath表达式实现标签定位和数据提取

    • 环境安装:

      • pip install lxml

    • etree对象实例化:

      • etree.parse('filePath')

      • etree.HTML(page_text)

    2. xpath表达式

    • xpath方法返回值是列表.

    • 最左侧如果为一个斜杠,则表示必须从跟节点开始进行标签定位

    • 最左侧为两个斜杠,表示可以从任意位置标签定位

    • 非最左侧的一个斜杠表示一个层级,两个斜杠表示多个层级

    • 属性定位://tagName[@attrName='value']

    • 索引定位://div[@class="tang"]/ul/li[2] 索引是从1开始

    • 取文本: /text() //text()

    • 取属性:/@attrName

    3. xpath案例

    1 .boss爬虫岗位爬取

    from lxml import etree
    import requests
    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://www.zhipin.com/c101010100/?query=python爬虫&page=%d'
    for page in range(1,5):
        new_url = format(url%page)
        page_text = requests.get(url=new_url,headers=headers).text
        # 解析
        tree = etree.HTML(page_text)
        li_list = tree.xpath('//*[@id="main"]/div/div[3]/ul/li')
        for li in li_list:
            job_name = li.xpath('./div/div[@class="info-primary"]/h3/a/div[1]/text()')[0]
            salary = li.xpath('./div/div[@class="info-primary"]/h3/a/span/text()')[0]
            company = li.xpath('./div/div[2]/div/h3/a/text()')[0]
            detail_url = 'https://www.zhipin.com'+li.xpath('./div/div[1]/h3/a/@href')[0]
            detail_page_text = requests.get(url=detail_url,headers=headers).text
            # 详情页工作描述
            tree = etree.HTML(detail_page_text)
            job_desc = tree.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()')
            job_desc = ''.join(job_desc)
            print(job_name,salary,company,job_desc)
    1. 中文乱码问题

    #通用的url模板(不可变)
    url = 'http://pic.netbian.com/4kdongwu/index_%d.html'
    for page in range(1,11):
        if page == 1:
            new_url = 'http://pic.netbian.com/4kdongwu/'
        else:
            new_url = format(url%page)
        response = requests.get(new_url,headers=headers)
    #     response.encoding = 'utf-8'
        page_text = response.text
        
        #解析:图片名称和图片的地址
        tree = etree.HTML(page_text)
        li_list = tree.xpath('//div[@class="slist"]/ul/li')
        for li in li_list:
            img_title = li.xpath('./a/img/@alt')[0]
            #通用解决中文乱码的处理方式
            img_title = img_title.encode('iso-8859-1').decode('gbk')
            img_src = 'http://pic.netbian.com'+li.xpath('./a/img/@src')[0] 

    3.xpath之管道符|

    #爬取https://www.aqistudy.cn/historydata/热门城市和全部城市的城市名称
    url = 'https://www.aqistudy.cn/historydata/'
    page_text = requests.get(url,headers=headers).text
    tree = etree.HTML(page_text)
    # #解析热门城市
    # hot_cities = tree.xpath('//div[@class="bottom"]/ul/li/a/text()')
    # #解析全部城市
    # all_cities = tree.xpath('//div[@class="bottom"]/ul/div[2]/li/a/text()')
    # print(all_cities)
    ​
    #好处:使得xpath表达式更具有通用性
    tree.xpath('//div[@class="bottom"]/ul/li/a/text() | //div[@class="bottom"]/ul/div[2]/li/a/text()')
    # 糗事百科中爬取作者名 匿名的处理
    url = 'https://www.qiushibaike.com/text/page/%d/'
    for page in range(1,10):
        print(str(page)+'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
        new_url = format(url%page)
        page_text = requests.get(url=new_url,headers=headers).text
        
        tree = etree.HTML(page_text)
        div_list = tree.xpath('//div[@id="content-left"]/div')
        for div in div_list:
            author = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()')[0]
            print(author)

    4. 图片懒加载

    • 图片懒加载是一种网页优化技术。图片作为一种网络资源,在被请求时也与普通静态资源一样,将占用网络资源,而一次性将整个页面的所有图片加载完,将大大增加页面的首屏加载时间。为了解决这种问题,通过前后端配合,使图片仅在浏览器当前视窗内出现时才加载该图片,达到减少首屏图片请求数的技术就被称为“图片懒加载”。

    • 网站一般如何实现图片懒加载技术呢?

    • 在网页源码中,在img标签中首先会使用一个“伪属性”(通常使用src2,original......)去存放真正的图片链接而并非是直接存放在src属性中。当图片出现到页面的可视化区域中,会动态将伪属性替换成src属性,完成图片的加载。

      import requests
      from lxml import etree
      url = 'http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html'
      page_text = requests.get(url,headers=headers).text
      for page in range(1,10):
          if page == 1:
              new_url = 'http://sc.chinaz.com/tag_tupian/YaZhouMeiNv.html'
      tree = etree.HTML(page_text)
      div_list = tree.xpath('//div[@id="container"]/div')
      for div in div_list:
          img_src = div.xpath('./div/a/img/@src2')[0]
          print(img_src)

    5. 爬取简历并持久化存储

    import os
    import requests
    from lxml import etree
    from urllib import request
    if not os.path.exists('./简历'):
        os.mkdir('./简历')
    url = 'http://sc.chinaz.com/jianli/free_%d.html'
    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"}
    for page in range(1,20):
        if page == 1:
            new_url = 'http://sc.chinaz.com/jianli/free.html'
        else:
            new_url = format(url%page)
        page_text = requests.get(url=new_url,headers=headers).text
        tree = etree.HTML(page_text)
        div_list = tree.xpath('//*[@id="container"]')
        for div in div_list:
            a_href = div.xpath('./div/a/@href')[0]
            a_name = div.xpath('./div/p/a/text()')[0]
            a_name = a_name.encode('iso-8859-1').decode('utf8')
            detail_text = requests.get(url=a_href,headers=headers).text
            tree = etree.HTML(detail_text)
            detail_url = tree.xpath('//*[@id="down"]/div[2]/ul/li[1]/a/@href')[0]
            jianli_name = a_name
            jianli_path = './简历/'+jianli_name+'.rar'
            request.urlretrieve(detail_url,jianli_path)
            print(jianli_name+'下载成功!')
     

    代理ip

    • 代理

    • cookie

    • 模拟登陆

      • 验证码的识别

    • 线程池在requests的应用

    • 单线程+多任务异步协程

    • 代理: 代理服务器

    • 基于代理的网站:

      • 站大爷

      • goubanjia

      • 快代理

      • 西祠代理

    • 代理的匿名度

      • 透明:使用透明代理,对方服务器可以知道你使用了代理,并且也知道你的真实IP

      • 匿名:对方服务器可以知道你使用了代理,但不知道你的真实IP。

      • 高匿:对方服务器不知道你使用了代理,更不知道你的真实IP

    • 类型:

      • http:代理服务器只可以转发http协议的请求

      • https:代理服务器只可以转发https协议的请求

    • 编码:

      • 在get或者post方法中应用一个proxies的参数,给其参数赋值为{'http':'ip:port'}

    cookie的处理方式

    • 处理方式:

      • 手动处理:将cookie的键值对手动添加到headers字典中,然后将headers作用到get或者post方法的headers参数中

      • 自动处理:使用Session。

        • session作用:session可以和requests一样进行请求的发送

        • 特性:使用session发起请求,如果请求过程中产生cookie,则cookie会被自动存储到session中

    url = 'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=-1&count=10&category=-1'
    #创建一个session对象
    session = requests.Session()
    #获取cookie
    session.get('https://xueqiu.com/',headers=headers)
    #携带cookie进行的请求发送
    session.get(url,headers=headers).json()

    模拟登陆

    • 什么是模拟登陆

      • 使用requests对登陆按钮的请求进行发送

    • 为什么要模拟登陆

      • 有的页面必须登陆之后才显示

    • 超级鹰的使用流程

      • 注册:用户中心身份的账户

      • 登陆:

        • 查看提分的剩余

        • 创建一个软件ID:软件ID-》生成一个软件ID(ID的名称和说明)

        • 下载示例代码:开发文档-》选择语言-》点击下载

     

    import requests from hashlib import md5
    class
    Chaojiying_Client(object): def __init__(self, username, password, soft_id): self.username = username password = password.encode('utf8') self.password = md5(password).hexdigest() self.soft_id = soft_id self.base_params = { 'user': self.username, 'pass2': self.password, 'softid': self.soft_id, } self.headers = { 'Connection': 'Keep-Alive', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', } ​ def PostPic(self, im, codetype): """ im: 图片字节 codetype: 题目类型 参考 http://www.chaojiying.com/price.html """ params = { 'codetype': codetype, } params.update(self.base_params) files = {'userfile': ('ccc.jpg', im)} r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers) return r.json() ​ def ReportError(self, im_id): """ im_id:报错题目的图片ID """ params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers) return r.json() #识别古诗文网中的验证码图片 url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx' headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36' } page_text = requests.get(url,headers=headers).text tree = etree.HTML(page_text) code_img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0] img_data = requests.get(code_img_src,headers=headers).content with open('./code.jpg','wb') as fp: fp.write(img_data) #使用线上平台识别验证码 chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370') #用户中心>>软件ID 生成一个替换 96001 im = open('code.jpg', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要// print(chaojiying.PostPic(im, 1902)['pic_str'])

    1.案例-基于古诗文网的模拟登陆

    #识别古诗文网中的验证码图片
    s = requests.Session()
    ​
    url = 'https://so.gushiwen.org/user/login.aspx?from=http://so.gushiwen.org/user/collect.aspx'
    page_text = s.get(url,headers=headers).text
    tree = etree.HTML(page_text)
    code_img_src = 'https://so.gushiwen.org'+tree.xpath('//*[@id="imgCode"]/@src')[0]
    #使用session对验证码图片发请求(会产生cookie)
    img_data = s.get(code_img_src,headers=headers).content
    with open('./code.jpg','wb') as fp:
        fp.write(img_data)
       
    # 解析出动态参数的数据值
    __VIEWSTATE = tree.xpath('//input[@id="__VIEWSTATE"]/@value')[0]
    __VIEWSTATEGENERATOR = tree.xpath('//input[@id="__VIEWSTATEGENERATOR"]/@value')[0]
        
    #使用线上平台识别验证码
    chaojiying = Chaojiying_Client('bobo328410948', 'bobo328410948', '899370')             # 用户中心>>软件ID 生成一个替换 96001
    im = open('code.jpg', 'rb').read()                              #  本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
    #验证码图片的文本数据
    code_img_text = chaojiying.PostPic(im, 1902)['pic_str']
    print(code_img_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": "www.zhangbowudi@qq.com",
        "pwd": "bobo328410948",
        "code": code_img_text,
        "denglu": "登录",
    }
    login_page_text = s.post(login_url,headers=headers,data=data).text
    with open('gushiwen.html','w',encoding='utf-8') as fp:
        fp.write(login_page_text)

    线程池的应用

    - 异步操作可以和非异步操作结合使用
    - 线程池最好只被应用在较为耗时的操作中

    同步:

    def request(url):
        print('正在请求:',url)
        time.sleep(2)
        print('请求成功:',url)
    start = time.time()
    urls = [
        'www.1.com',
        'www.2.com',
        'www.3.com',
        'www.4.com',
    ]
    for url in urls:
        request(url)    
    print(time.time()-start)

    基于异步的操作:

    from multiprocessing.dummy import Pool
    start = time.time()
    pool = Pool(4)
    def request(url):
        print('正在请求:',url)
        time.sleep(2)
        print('请求成功:',url)
    urls = [
        'www.1.com',
        'www.2.com',
        'www.3.com',
        'www.4.com',
    ]
    pool.map(request,urls)
    pool.close()
    pool.join()
    print(time.time()-start)

    2.案例-爬取梨视频的短视频数据

    import requests
    from lxml import etree
    import re
    #爬取梨视频的短视频数据
    #var contId="1570697",liveStatusUrl="liveStatus.jsp",liveSta="",
    # playSta="1",autoPlay=!1,isLiving=!1,isVrVideo=!1,hdflvUrl="",sdflvUrl="",hdUrl="",sdUrl="",ldUrl="",
    # srcUrl="https://video.pearvideo.com/mp4/adshort/20190626/cont-1570697-14061078_adpkg-ad_hd.mp4",vdoUrl=srcUrl,skinRes="//www.pearvideo.com/domain/skin",videoCDN="//video.pearvideo.com";
    pool = Pool(4)
    ​
    url = 'https://www.pearvideo.com/category_1'
    page_text = requests.get(url,headers=headers).text
    tree = etree.HTML(page_text)
    li_list = tree.xpath('//*[@id="listvideoListUl"]/li')
    all_video_urls = []
    for li in li_list:
        detail_url = 'https://www.pearvideo.com/'+li.xpath('./div/a/@href')[0]
        detail_page_text = requests.get(detail_url,headers=headers).text
        #解析出视频的url
        ex = 'srcUrl="(.*?)",vdoUrl'
        video_url = re.findall(ex,detail_page_text,re.S)[0]
        all_video_urls.append(video_url)
        
    def dowmload_save(url):
        video_data = requests.get(url,headers=headers).content
        fileName = url.split('/')[-1]
        with open(fileName,'wb') as fp:
            fp.write(video_data)
        print(fileName,'下载成功')
        
    #视频数据的请求和持久化存储是比较耗时的,可以基于线程池来实现
    pool.map(dowmload_save,all_video_urls)#参数1对应的函数必须只可以有一个参数
     

    多任务异步协程(并发)

    - 协程:对象。如果一个函数在定义的时候被async修饰了,则该函数被调用的时候会返回一个协程对象
    ,函数内部的程序语句不会其调用的时候被执行(特殊的函数)

    - 任务对象:对象,就是对协程的进一步封装。任务对象==协程==特殊的函数.任务对象中可以显示
    协程的相关状态。任务对象可以被绑定一个回调。
    - 绑定回调:

    - 事件循环:无限(不确定循环次数)的循环。需要向其内部注册多个任务对象(特殊的函数)。
    - async:专门用来修饰函数
    - await:挂起

    - requests和aiohttp 的区别
    - session.get、post(url,headers,params/data,proxy="http://ip:port")    ###  aiohttp
    - response.text()、json().read()

    案例1 多任务异步协程爬取音频

    import aiohttp
    import requests
    import asyncio
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
    }
    url = 'https://www.ximalaya.com/revision/play/album?albumId=11219576&pageNum=1&sort=1&pageSize=30'
    json_data = requests.get(url, headers=headers).json()
    
    urls = []  # 30个音频的url和name
    for dic in json_data['data']['tracksAudioPlay']:
        audio_url = dic['src']
        audio_name = dic['trackName']
        urls.append({'name': audio_name, 'url': audio_url})
    
    
    # 定义特殊的修饰函数用来发送请求
    async def request(dic):
        async with aiohttp.ClientSession() as s:
            # s是aiohttp中的一个请求对象
            # await:阻塞操作对应的代码中(请求,获取响应数据)
            # async:只要是跟aiohttp相关联的代码前
            # proxy='http://ip:port' 代理操作
            async with await s.get(dic['url'], headers=headers) as response:
                # text()字符串形式的响应数据
                # json(),read()二进制类型的数据
                audio_data = await response.read()
                name = dic['name']
                return {'data': audio_data, 'name': name}
    
    
    # 回调函数的封装:必须有一个task的参数
    def saveData(task):
        print('开始保存')
        dic = task.result()  # 音频的数据和名字  async修饰函数的返回值
        filename = dic['name'] + '.m4a'  #
        data = dic['data']
        with open(f'./相声/{filename}', 'wb') as f:
            f.write(data)
        print(filename, '下载完成!!')
    
    
    tasks = []  # 任务列表
    for dic in urls:
        # 协程
        c = request(dic)
        # 任务对象
        task = asyncio.ensure_future(c)
        task.add_done_callback(saveData)
        tasks.append(task)
    # 创建事件循环对象,然后进行任务对象的注册,且启动事件循环
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))

    selenium

    使用实例 京东joy

    from selenium import webdriver
    from time import sleep
    # 实例化了一个谷歌浏览器对象且将驱动程序进行了加载
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    # 发起请求
    bro.get('https://hellojoy.jd.com/')
    # 使用find系列的函数进行标签定位
    input_tag = bro.find_element_by_id('key')
    # 进行节点交互
    input_tag.send_keys('macbook')
    sleep(2)
    # 搜索按钮的定位
    btn = bro.find_element_by_xpath('//*[@id="search-2014"]/div/button')
    btn.click()
    sleep(10)
    # 执行js,实现滑动窗口
    bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
    sleep(2)
    bro.execute_script('window.scrollTo(0,document.body.scrollHeight)')
    sleep(2)
    
    bro.close()

    案例1 使用selenuim对动态数据进行爬取数据

    # 动态请求数据
    from bs4 import BeautifulSoup
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    bro.get('http://125.35.6.84:81/xk/')
    # 获取当前页面中展示的企业名称
    # 获取浏览器打开页面的页面源码数据(可见即可爬)
    page_text = bro.page_source
    # 使用bs4 解析企业名称
    soup = BeautifulSoup(page_text,'lxml')
    dl_list = soup.select('#gzlist > li > dl')
    for dl in dl_list:
        name = dl.string
        print(name)
    sleep(2)
    bro.close()

    案例2 使用selenuim多页处理

    #处理多页
    from selenium import webdriver
    from time import sleep
    #实例化了一个谷歌浏览器对象且将驱动程序进行了加载
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    bro.get('http://125.35.6.84:81/xk/')
    alist = [] #存放不同页码对应的页面源码数据(page_source)
    #获取当前页面中展示的企业名称
    sleep(2)
    #获取浏览器打开页面的页面源码数据(可见即可爬)
    page_text = bro.page_source
    alist.append(page_text)
    id_value_model = 'pageIto_first%d'
    for page in range(2,8):
        id_value = format(id_value_model%page)
        btn = bro.find_element_by_id(id_value)
        btn.click()
        sleep(3)
        page_text = bro.page_source
        alist.append(page_text)
        sleep(2)    
    for page_text in alist:
        sleep(1)
        soup = BeautifulSoup(page_text,'lxml')
        dl_list = soup.select('#gzlist > li > dl')
        for dl in dl_list:
            name = dl.string
            print(name)        
    bro.quit()

    案例3 模拟登陆

    #模拟登陆
    from selenium import webdriver
    from time import sleep
    #实例化了一个谷歌浏览器对象且将驱动程序进行了加载
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    
    bro.get('https://qzone.qq.com/')
    #分析发现定位的a标签是出现在iframe标签之下,则必须通过switch_to.frame操作后,才可以进行标签定位
    bro.switch_to.frame('login_frame')
    
    a_tag = bro.find_element_by_id('switcher_plogin')
    a_tag.click()
    sleep(2)
    bro.find_element_by_id('u').send_keys('123456')
    sleep(2)
    bro.find_element_by_id('p').send_keys('XXXXXXXXXXXXX')
    sleep(2)
    bro.find_element_by_id('login_button').click()
    
    #登陆成功后的页面源码数据
    page_text = bro.page_source
    sleep(2)
    bro.quit()

    不太常用的操作

    • 动作链

    from selenium import webdriver
    from selenium.webdriver import ActionChains
    from time import sleep
    #实例化了一个谷歌浏览器对象且将驱动程序进行了加载
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    
    bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
    
    #基于动作链实现拖动操作
    bro.switch_to.frame('iframeResult')
    #定位即将被拖动的标签
    div_tag = bro.find_element_by_id('draggable')
    
    #实例化一个动作链对象,将当前浏览器对象作为参数进行传入
    action = ActionChains(bro)
    #点击且长按的操作
    action.click_and_hold(div_tag)
    
    for i in range(5):
        #perform()表示立即执行动作链
        action.move_by_offset(15,0).perform()
        sleep(0.5)    
    action.release()
    sleep(2)
    bro.quit()
    • phantomJs:是一个无可视化界面的浏览器

    • 谷歌无头浏览

    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')
    
    
    #实例化了一个谷歌浏览器对象且将驱动程序进行了加载
    bro = webdriver.Chrome(executable_path='./chromedriver.exe',chrome_options=chrome_options)
    bro.get('https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
    
    bro.save_screenshot('./1.png')
    
    print(bro.page_source)

    如何规避selenium被检测的风险

    from selenium.webdriver import Chrome
    from selenium.webdriver import ChromeOptions
    
    
    option = ChromeOptions()
    option.add_experimental_option('excludeSwitches', ['enable-automation'])
    
    driver = Chrome(executable_path='./chromedriver.exe',options=option)

    案例4 12306模拟登陆

    超级鹰接口
    import requests
    from hashlib import md5
    
    class Chaojiying_Client(object):
    
        def __init__(self, username, password, soft_id):
            self.username = username
            password = password.encode('utf8')
            self.password = md5(password).hexdigest()
            self.soft_id = soft_id
            self.base_params = {
                'user': self.username,
                'pass2': self.password,
                'softid': self.soft_id,
            }
            self.headers = {
                'Connection': 'Keep-Alive',
                'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
            }
    
        def PostPic(self, im, codetype):
            """
            im: 图片字节
            codetype: 题目类型 参考 http://www.chaojiying.com/price.html
            """
            params = {
                'codetype': codetype,
            }
            params.update(self.base_params)
            files = {'userfile': ('ccc.jpg', im)}
            r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
                              headers=self.headers)
            return r.json()
    
        def ReportError(self, im_id):
            """
            im_id:报错题目的图片ID
            """
            params = {
                'id': im_id,
            }
            params.update(self.base_params)
            r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
            return r.json()
    from selenium import webdriver
    import time
    import requests
    from selenium.webdriver import ActionChains
    from PIL import Image
    
    # Pillow  == PIL
    
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    bro.get('https://kyfw.12306.cn/otn/login/init')
    
    time.sleep(3)
    
    # 定位到了img标签(验证码),想要通过img标签获取验证码图片的左上角和右下角两点坐标
    code_img_ele = bro.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
    time.sleep(3)
    # 获取验证码图片的左上角和右下角两点坐标
    location = code_img_ele.location  # 验证码图片左上角坐标
    print(location, ':左上角坐标!')
    
    size = code_img_ele.size  # 返回的是验证码的尺寸(长和宽)
    print(size, ':size的值')
    # 矩形区域:表示的就是验证码图片的区域(裁剪的区域)
    rangle = (
        int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
    
    # 将浏览器打开的登录页面进行整体截图
    bro.save_screenshot('aa.png')
    
    i = Image.open('./aa.png')
    # 验证码图片的名称
    code_img_name = 'code.png'
    # 根据制定好的矩形区域(左上和右下两点坐标)进行验证码图片的裁剪
    frame = i.crop(rangle)
    
    frame.save(code_img_name)
    
    chaojiying = Chaojiying_Client('martin144', 'martin144', '900215')
    
    # 用户中心>>软件ID 生成一个替换 96001
    im = open('./code.png', 'rb').read()
    result = chaojiying.PostPic(im, 9004)['pic_str']
    # x1,y1      x1,y1|x2,y2   55,99
    #  x1,y1|x2,y2  ==》 [[x1,y1],[x2,y2]]
    all_list = []  # 存储的是超级鹰返回的坐标数据
    if '|' in result:
        list_1 = result.split('|')
        count_1 = len(list_1)
        for i in range(count_1):
            xy_list = []
            x = int(list_1[i].split(',')[0])
            y = int(list_1[i].split(',')[1])
            xy_list.append(x)
            xy_list.append(y)
            all_list.append(xy_list)
    else:
        x = int(result.split(',')[0])
        y = int(result.split(',')[1])
        xy_list = []
        xy_list.append(x)
        xy_list.append(y)
        all_list.append(xy_list)
    print(all_list)
    
    # 根据all_list中的数据进行点击操作
    action = ActionChains(bro)
    
    for l in all_list:
        x = l[0]
        y = l[1]
        action.move_to_element_with_offset(code_img_ele, x, y).click().perform()
        time.sleep(1)
    
    bro.find_element_by_id('username').send_keys('123ertghjk') # 12306账号
    time.sleep(2)
    bro.find_element_by_id('password').send_keys('asdfghjka') # 12306密码
    time.sleep(2)
    bro.find_element_by_id('loginSub').click()
    time.sleep(10)
    bro.quit()
     
  • 相关阅读:
    防止SSH自动断线
    php intval()和floatval()
    开启 intel vt-d
    前用户sudo免密码
    分区工具parted的详解及常用分区使用方法
    Proxmox qm命令应用实例
    Mac上制作Centos7系统U盘安装盘
    FinalShell Mac OS版,Linux版安装及教程
    python爬虫彩票案例,并自动发微信
    Python利用itchat库向好友或者公众号发消息
  • 原文地址:https://www.cnblogs.com/martin-yz/p/11085332.html
Copyright © 2011-2022 走看看