Requests-基础

摘要

本文内容转自网络,个人学习记录使用,请勿传播

爬虫初始

为什么要学习爬虫

之前在授课过程中,好多同学都问过我这样的一个问题:为什么要学习爬虫,学习爬虫能够为我们以后的发展带来那些好处?其实学习爬虫的原因和为我们以后发展带来的好处都是显而易见的,无论是从实际的应用还是从就业上。

我们都知道,当前我们所处的时代是大数据的时代,在大数据时代,要进行数据分析,首先要有数据源,而学习爬虫,可以让我们获取更多的数据源,并且这些数据源可以按我们的目的进行采集。

优酷推出的火星情报局就是基于网络爬虫和数据分析制作完成的。其中每期的节目话题都是从相关热门的互动平台中进行相关数据的爬取,然后对爬取到的数据进行数据分析而得来的。另一方面,优酷根据用户实时观看视频时的前进,后退等行为数据,能够推测计算出观众的兴趣点和爱好点,这样有助于节目的剪辑和后期的节目方案的编写。

今日头条作为一个新闻推荐类的应用,其内部的新闻数据都是通过爬虫程序在各个新闻网站进行新闻数据的爬取,然后通过相应的处理和运算将用户感兴趣的新闻话题推送到用户的手机上。

从就业的角度来说,爬虫工程师目前来说属于紧缺人才,并且薪资待遇普遍较高所以,深层次地掌握这门技术,对于就业来说,是非常有利的。有些人学习爬虫可能为了就业或者跳槽。从这个角度来说,爬虫工程师是不错的选择之一。随着大数据时代的来临,爬虫技术的应用将越来越广泛,在未来会拥有更好的发展空间。

爬虫相关介绍

  • 什么是爬虫
    • 就是编写程序,模拟浏览器上网,让其去互联网中抓取数据的过程
      • 模拟:
        • 浏览器本身就是一个纯天然的爬虫工具,爬虫相关的模块都是基于浏览器为基础开发出来的。
        • 注意:日后只要是你的爬虫程序没有爬取到你想要的数据,只有一个原因:
          • 就是你的爬虫程序模拟的力度不够!
      • 抓取:
        • 抓取网页数据分两种情况:
          • 将一个页面所有的数据抓取到
          • 将页面中局部的数据抓取到
  • 爬虫在应用场景的分类
    • 通用爬虫:
      • 将一个页面中所有的数据获取。
      • 大部分的搜索引擎中应用比较多。
    • 聚焦爬虫
      • 将页面中局部的指定的数据进行提取/抓取
      • 注意:聚焦爬虫一定是建立在通用爬虫的基础之上实现。
    • 功能爬虫
      • 通过浏览器或者app自动化的操作,实现相关的网页或者app自动化的操作。代替人工在网页或者手机软件中自动执行相关的行为动作。
      • 批量点赞,批量评论,刷单,秒杀…..
    • 增量式爬虫
      • 用来监测网站数据更新的情况。以便爬取网站最新更新出来的数据!
    • 分布式爬虫
      • 可以对网站所有的资源使用分布式机群进行分布和联合的数据爬取
  • 爬虫的矛与盾
    • 反爬机制:对应门户网站,网站可以指定相关的机制阻止爬虫对其网站数据的采集
    • 反反爬策略:对应爬虫程序,爬虫可以制定相关的策略将网站的反爬机制破解,从而爬取到指定的数据
  • 盗亦有道的君子协议robots
    • Robots协议(也称为爬虫协议、机器人协议等)的全称是“网络爬虫排除标准”(Robots ExclusionProtocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取.
    • 注意,这个协议的存在更多的是需要网络爬虫去遵守,而起不到防止爬虫的功能。

爬虫合法性探究

  • 爬虫作为一种计算机技术就决定了它的中立性,因此爬虫本身在法律上并不被禁止,但是利用爬虫技术获取数据这一行为是具有违法甚至是犯罪的风险的。所谓具体问题具体分析,正如水果刀本身在法律上并不被禁止使用,但是用来捅人,就不被法律所容忍了。
  • 或者我们可以这么理解:爬虫是用来批量获得网页上的公开信息的,也就是前端显示的数据信息。因此,既然本身就是公开信息,其实就像浏览器一样,浏览器解析并显示了页面内容,爬虫也是一样,只不过爬虫会批量下载而已,所以是合法的。不合法的情况就是配合爬虫,利用黑客技术攻击网站后台,窃取后台数据(比如用户数据等)。
  • 举个例子:像谷歌这样的搜索引擎爬虫,每隔几天对全网的网页扫一遍,供大家查阅,各个被扫的网站大都很开心。这种就被定义为“善意爬虫”。但是像抢票软件这样的爬虫,对着 12306 每秒钟恨不得撸几万次,铁总并不觉得很开心,这种就被定义为“恶意爬虫”。
  • 爬虫所带来风险主要体现在以下3个方面:
    • 1、违反网站意愿,例如网站采取反爬措施后,强行突破其反爬措施;
    • 2、爬虫干扰了被访问网站的正常运营;
    • 3、爬虫抓取了受到法律保护的特定类型的数据或信息。
  • 那么作为爬虫开发者,如何在使用爬虫时避免进局子的厄运呢?
    • 1、严格遵守网站设置的robots协议;
    • 2、在规避反爬虫措施的同时,需要优化自己的代码,避免干扰被访问网站的正常运行;
    • 3、在使用、传播抓取到的信息时,应审查所抓取的内容,如发现属于用户的个人信息、隐私或者他人的商业秘密的,应及时停止并删除。
  • 总结:
    • 可以说在我们身边的网络上已经密密麻麻爬满了各种网络爬虫,它们善恶不同,各怀心思。而越是每个人切身利益所在的地方,就越是爬满了爬虫。所以爬虫是趋利的,它们永远会向有利益的地方爬行。技术本身是无罪的,问题往往出在人无限的欲望上。因此爬虫开发者的道德自持和企业经营者的良知才是避免触碰法律底线的根本所在。

requests基础操作

  • 基本介绍

    • requests就是爬虫中一个基于网络请求的模块。
    • 作用:模拟浏览器上网的。
    • urllib模块就是一个老版的requests模块,现在没人用urllib
  • 环境安装

    • pip install requests
  • 编码流程

    • 指定url(好比打开浏览器输入网址)
    • 发起请求(好比是按下回车)
    • 获取响应数据(从指定url中爬取到的数据)
    • 持久化存储
  • 案例应用

    • 搜狗首页数据采集

      • https://www.sogou.com/

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        import requests

        #1,指定url
        url = 'https://www.sogou.com/'

        #2,发起请求(只要在浏览器地址栏输入网址按下回车,发起的一定get请求)
        #url为get方法的第一个参数,表示根据指定的url发起get请求
        #get方法会返回一个响应对象
        response = requests.get(url=url) #get使用来使用requests模块发起get请求

        #3.获取响应数据/爬取到的数据
        page_text = response.text #text属性使用来返回字符串形式的响应数据
        # print(page_text)

        #4.持久化存储
        with open('./sogou.html','w') as fp:
        fp.write(page_text)
        print('数据爬取存储成功!')
    • 简易的网页采集器

      • 注意:在浏览器的地址栏中网址,网址?后面的内容就是请求的参数(请求参数)

      • https://www.sogou.com/

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        import requests

        #请求参数动态化
        keyword = input('请输入关键字:')
        #稍后想要把该字典作为请求参数
        pram = {
        'query':keyword, #只存在一个键值对(存在一组请求参数)
        }
        #1.指定url
        url = 'https://www.sogou.com/web' #需要将请求参数去除
        #2.发起请求
        #params参数就是用来在请求时携带指定的请求参数
        response = requests.get(url=url,params=pram)

        #3.获取响应数据
        page_text = response.text

        #4.持久化存储
        fileName = keyword + '.html'
        with open(fileName,'w') as fp:
        fp.write(page_text)


        #出问题:没有爬取到我们想要的数据?原因是因为遇到反爬机制
      • 分析该网站的反爬机制:

        • 从爬取到的内容中提取到了一个关键信息:网站检测到了异常的访问请求
          • 异常的访问请求:通过程序发起的请求
          • 正常访问请求:通过浏览器发起的请求
        • 网站如何可以监测请求是不是通过浏览器发起的呢?
          • 是通过请求的一个头信息:user-agent
          • user-agent:请求载体的身份标识
        • 破解方式(UA伪装):伪装请求载体的身份标识
          • 该反爬机制是一种最常见最通用的,也就是说绝大数网站都会携带该反爬机制,因此日后写爬虫程序,默认带上UA伪装操作。
      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        import requests

        #请求参数动态化
        keyword = input('请输入关键字:')
        #稍后想要把该字典作为请求参数
        pram = {
        'query':keyword, #只存在一个键值对(存在一组请求参数)
        }
        #1.指定url
        url = 'https://www.sogou.com/web' #需要将请求参数去除
        #2.发起请求
        head = { #存放需要伪装的头信息
        'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
        }
        #通过headers参数进行了头信息的伪装
        response = requests.get(url=url,params=pram,headers=head)

        #3.获取响应数据
        page_text = response.text

        #4.持久化存储
        fileName = keyword + '.html'
        with open(fileName,'w') as fp:
        fp.write(page_text)
    • 豆瓣电影

      • https://movie.douban.com/typerank?type_name=%E7%88%B1%E6%83%85&type=13&interval_id=100:90&action=

        • 爬取电影的详情数据

        • 对网站进行分析:

          • 1.在抓包工具中先定位到和浏览器地址栏的网址一样的数据包

          • 2.查看开发者工具中,定位到的数据包中的response这一项,该项中存放的就是对定位到数据包的url发起请求,请求到的数据。

          • 3.在response选项卡中查看是否存在我们想要爬取的数据:

            • 如何检测是否存在我们想要爬取的数据呢?

              • 局部搜索:将你想要爬取的局部数据,在response进行搜索,查看是否可以搜索到。

                • 搜索到了:

                  • 可以直接对该数据包的url发起请求获取你想要的数据即可
                • 搜索不到:

                  • 说明你想要的数据是【动态加载数据】
                  • 什么是动态加载数据?
                    • 特指,不是通过浏览器地址栏的请求请求到的数据,就是动态加载数据。同理,动态加载数据一定是通过其他的请求请求到的。
                  • 如何获取动态加载数据?
                    • 基于抓包工具进行全局搜索
                      • 鼠标点击任意的数据包,然后按下cotrl+f打开全局搜索框,搜索局部你想要的数据,即可定位到包含搜索数据的指定数据包。
                • 1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14
                  15
                  16
                  17
                  18
                  19
                  20
                  21
                  22
                  import requests
                  head = { #存放需要伪装的头信息
                  'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
                  }
                  pram = {
                  "type": "13",
                  "interval_id": "100:90",
                  "action": "",
                  "start": "0",
                  "limit": "20",
                  }
                  url = 'https://movie.douban.com/j/chart/top_list'
                  response = requests.get(url=url,headers=head,params=pram)
                  #获取响应数据
                  #json()可以将获取到的json格式的字符串进行反序列化
                  page_text = response.json()
                  fp = open('./douban.txt','w')
                  for dic in page_text:
                  title = dic['title']
                  score = dic['score']
                  fp.write(title+':'+score+'\n')
                  print(title,'爬虫保存成功!')
  • 肯德基

    • http://www.kfc.com.cn/kfccda/index.aspx

      • 将餐厅的位置信息进行数据爬取

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        import requests
        head = { #存放需要伪装的头信息
        'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
        }
        #post请求的请求参数
        data = {
        "cname": "",
        "pid": "",
        "keyword": "天津",
        "pageIndex": "1",
        "pageSize": "10",
        }
        #在抓包工具中:Form Data存放的是post请求的请求参数,而Query String中存放的是get请求的请求参数
        url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
        #在post请求中,处理请求参数的是data这个参数不是params
        response = requests.post(url=url,headers=head,data=data)
        #将响应数据进行反序列化
        page_text = response.json()
        for dic in page_text['Table1']:
        name = dic['storeName']
        addr = dic['addressDetail']
        print(name,addr)
  • 药监总局(作业)

    • http://scxk.nmpa.gov.cn:81/xk/

    • 要求:抓取每一家企业的企业详情数据

    • 思路:

      • 1.在对一个陌生的网站进行数据爬取前,首先要确定我们想要爬取的数据是否为动态加载数据?

        • 先进入到任意一家企业的详情页中,查看企业的详情数据是否为动态加载数据?

          • 基于抓包工具进行局部搜索
            • 搜索的到:不是动态加载
            • 搜索不到:是动态加载
              • 发现企业的详情数据是动态加载数据
        • 如何捕获动态加载数据?

          • 基于抓包工具进行全局搜索,定位到动态加载数据对应的数据包

            • url:http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById

            • 请求方式:POST

            • 请求参数:id: d601af664b5940029601d6d0a05be321

            • 成功获取了一家企业对应的企业详情数据

            • 1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              import requests
              #指定url
              url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
              #请求参数
              data = {
              'id':'d601af664b5940029601d6d0a05be321'
              }
              #UA伪装
              headers = {
              'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36X-Requested-With: XMLHttpRequest'
              }
              #发起的post请求
              response = requests.post(url=url,data=data,headers=headers)
              #获取响应数据
              json_data = response.json() #如果确定响应数据为json格式字符串才可以调用json方法实现反序列化
              #获取企业名称,法人代表,许可证编号
              print(json_data['epsName'],json_data['legalPerson'],json_data['productSn'])
            • 再次对另一家企业的企业详情数据进行分析

              • 定位到了动态加载数据对应的数据包
                • 在该数据包中可以提取到url,请求方式和请求参数,对比发现,不同企业的详情数据的数据包请方式和url是一样的,只有请求参数id的值不一样。
                • 结论:不同企业的企业详情数据对应的数据包只有id的参数不同剩下都一样。
                • 结果:如果我们可以批量获取多家企业的id值,就可以批量获取多家企业的企业详情数据。
            • 如何批量获取多家企业的id值?

              • id通常表示一组数据的唯一标识。联想到企业的名称也会作为企业的唯一标识,那么会不会企业的id和企业的名称在页面中是绑定在一起的呢?

                • 测试:在首页通过企业名称找到企业的id

                  • 通过抓包工具的分析,首页中企业的名称等信息也是动态加载数据。

                    • 捕获动态加载数据。定位到指定的数据包,从数据包的响应数据中发现了不同企业的id,就可以将不同企业的id取到。

                    • 1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      18
                      19
                      20
                      import requests
                      #UA伪装
                      headers = {
                      'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36X-Requested-With: XMLHttpRequest'
                      }
                      main_url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
                      m_data = {
                      "on": "true",
                      "page": "1",
                      "pageSize": "15",
                      "productName": "",
                      "conditionType": "1",
                      "applyname": "",
                      "applysn": "",
                      }
                      m_response = requests.post(url=main_url,headers=headers,data=m_data)
                      json_data = m_response.json()
                      for dic in json_data['list']:
                      _id = dic['ID']
                      print(_id)
              • 整体代码整合:

                • 1
                  2
                  3
                  4
                  5
                  6
                  7
                  8
                  9
                  10
                  11
                  12
                  13
                  14
                  15
                  16
                  17
                  18
                  19
                  20
                  21
                  22
                  23
                  24
                  25
                  26
                  27
                  28
                  29
                  30
                  31
                  32
                  33
                  34
                  35
                  36
                  import requests
                  #UA伪装
                  headers = {
                  'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36X-Requested-With: XMLHttpRequest'
                  }
                  main_url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
                  m_data = {
                  "on": "true",
                  "page": "1",
                  "pageSize": "15",
                  "productName": "",
                  "conditionType": "1",
                  "applyname": "",
                  "applysn": "",
                  }
                  m_response = requests.post(url=main_url,headers=headers,data=m_data)
                  json_data = m_response.json()
                  ids = [] #存储多家企业的id
                  for dic in json_data['list']:
                  _id = dic['ID']
                  ids.append(_id)

                  #循环对每一家企业的详情数据进行获取
                  for _id in ids:
                  #指定url
                  url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
                  #请求参数
                  data = {
                  'id':_id
                  }
                  #发起的post请求
                  response = requests.post(url=url,data=data,headers=headers)
                  #获取响应数据
                  json_data = response.json() #如果确定响应数据为json格式字符串才可以调用json方法实现反序列化
                  #获取企业名称,法人代表,许可证编号
                  print(json_data['epsName'],json_data['legalPerson'],json_data['productSn'])