requests基础

爬虫初始

爬虫相关介绍

  • 什么是爬虫

    • 就是通过编写程序,“模拟”浏览器上网,然后让其在互联网中“抓取”数据的过程。

    • 还没有一个共同定义的概念

      • 模拟:浏览器本身就是一个纯天然的爬虫工具。爬虫相关的操作都是模拟/基于浏览器为基础开发实现出来的。

      • 抓取:

        • 一种是抓取一张页面中所有的数据

        • 一种是抓取页面中局部的数据

    • 提问:如果日后你的爬虫程序没有爬取到你想要的数据,why?

      • 你的程序模拟浏览器的力度不够!

  • 爬虫在应用场景的分类

    • 通用爬虫:将一整张页面源码数据进行爬取。

    • 聚焦爬虫:将一张页面中局部/指定的数据进行抓取。建立在通用爬虫的基础上。

    • 功能爬虫:通过浏览器或者app自动化的操作,实现相关的网页或者app自动化的操作。代替人工在网页或者手机软件中自动执行相关的行为动作。(写的爬虫程序可以模拟人的行为动作)

    • 增量式爬虫:用来监测网站数据更新的情况(便于将最新更新出来的数据进行抓取)

    • 分布式爬虫:可以搭建分布式机群对一个网络资源进行联合且分布的数据爬取。

  • 爬虫的矛与盾(重点)

    • 反爬机制:对应门户网站,网站可以指定相关的机制阻止爬虫对其网站数据的采集

    • 反反爬策略:对应爬虫程序,爬虫可以制定相关的策略将网站的反爬机制破解,从而爬取到指定的数据

  • 盗亦有道的君子协议robots

    • Robots协议(也称为爬虫协议、机器人协议等)的全称是“网络爬虫排除标准”(Robots ExclusionProtocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取.

    • 注意,这个协议的存在更多的是需要网络爬虫去遵守,而起不到防止爬虫的功能。

    • 淘宝的君子协议robots

    • 淘宝的robots:https://www.taobao.com/robots.txt公开在外网的,任何人都可以访问它。 User-agent: Baiduspider

    • Disallow: / # Disallow不允许,allow允许 User-agent: baiduspider

    • Disallow: / # 网站中的所有数据都不运行爬取

爬虫合法性探究

  • 爬虫作为一种计算机技术就决定了它的中立性,因此爬虫本身在法律上并不被禁止,但是利用爬虫技术获取数据这一行为是具有违法甚至是犯罪的风险的。所谓具体问题具体分析,正如水果刀本身在法律上并不被禁止使用,但是用来捅人,就不被法律所容忍了。

  • 或者我们可以这么理解:爬虫是用来批量获得网页上的公开信息的,也就是前端显示的数据信息。因此,既然本身就是公开信息,其实就像浏览器一样,浏览器解析并显示了页面内容,爬虫也是一样,只不过爬虫会批量下载而已,所以是合法的。不合法的情况就是配合爬虫,利用黑客技术攻击网站后台,窃取后台数据(比如用户数据等)。

  • 举个例子:像谷歌这样的搜索引擎爬虫,每隔几天对全网的网页扫一遍,供大家查阅,各个被扫的网站大都很开心。这种就被定义为“善意爬虫”。但是像抢票软件这样的爬虫,对着 12306 每秒钟恨不得撸几万次,铁总并不觉得很开心,这种就被定义为“恶意爬虫”。

  • 爬虫所带来风险主要体现在以下3个方面:

    • 1、违反网站意愿,例如网站采取反爬措施后,强行突破其反爬措施;

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

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

  • 那么作为爬虫开发者,如何在使用爬虫时避免进局子的厄运呢?

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

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

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

  • 总结:

    • 可以说在我们身边的网络上已经密密麻麻爬满了各种网络爬虫,它们善恶不同,各怀心思。而越是每个人切身利益所在的地方,就越是爬满了爬虫。所以爬虫是趋利的,它们永远会向有利益的地方爬行。技术本身是无罪的,问题往往出在人无限的欲望上。因此爬虫开发者的道德自持和企业经营者的良知才是避免触碰法律底线的根本所在。

  • 经验:一、gov(政府网站),后缀是gov的网站,尽量不要取爬(敏感数据,涉嫌泄密)。二、尽量不要侵犯别人的利益(如付费的数据)。三、爬国外的网站要FQ,FQ是违法的(被监管局发现是要负法律责任的)。

  • 爬虫的厉害之处:有人说,在IT行业,你能想到的功能和想不到的功能,爬虫都能实现。因为它最大的优势在于拥有大量强大的官方的、第三方的数据库(包、模块(工具))。

requests基础操作(代码实操重点!!!)

基本介绍

  • requests是一个基于网络请求的模块。可以使用程序模拟浏览器上网。

环境安装

  • pip install requests

  • WIN+R->cmd->pip install requests

  • pycharm->Terminal(终端)->pip install requests

编码流程

  • 指定url(输入一个网址)

  • 发起请求(按下回车)

  • 获取响应数据(请求到的数据),响应体中存放的就是请求到的数据

  • 持久化存储

    如果出现‘名称’、‘状态’等栏中如果选择了‘全部’并刷新网页后任然没有信息的时候,要注意在过滤框中是不是输入了信息,如果有,清除后应该就回复正常了。

案例应用

  • 东方财富首页数据爬取

    https://www.eastmoney.com/

    # Tom老师的编码
    import requests
    ​
    # 1.指定url
    main_url = 'https://www.eastmoney.com/'
    ​
    # 2.发起请求:
    # get函数可以根据指定的url发起网络请求
    # get函数会返回一个响应对象:
    response = requests.get(url=main_url)
    ​
    # 3.获取响应数据
    page_text = response.text #text是可以返回字符串形式的响应数据/爬取到的数据
    ​
    # 4.持久化存储
    with open('dongfang.html','w') as fp:  # Tom老师用的是MOC,不要加encoding,不报错。
        fp.write(page_text)

发现:爬取的页面数据出现了中文乱码。

字符集编码:utf-8 gbk等,计算机只能存二进制数据,现在爬取的数据都是非二进制数据,要用字符集编码把它变成二进制数据。

出现乱码的原因是保存的内容编码有问题,而保存的内容来源于获取的响应数据,响应数据又是来源于响应对象,所以设置响应对象的编码格式就行。

response.encoding = 'utf-8'或response.encoding = 'gbk'或其他编码格式。

中文乱码解决

import requests
​
# 1.指定url
main_url = 'https://www.eastmoney.com/'
​
# 2.发起请求:
# get函数可以根据指定的url发起网络请求
# get函数会返回一个响应对象:
response = requests.get(url=main_url)  # response,响应对象
​
# 设置响应对象的编码格式(处理中文乱码)
response.encoding = 'utf-8'
# 搜索:charset
​
# 3.获取响应数据
page_text = response.text #text是可以返回字符串形式的响应数据/爬取到的数据。response.text是响应数据。
​
# 4.持久化存储
with open('dongfang.html','w') as fp:  # Tom老师用的是MOC,不要加encoding,不报错。
    fp.write(page_text)
​
  # 我的编码
import requests
  # 1.指定url
  main_url = 'https://www.eastmoney.com/'
  
  # 2.发起请求:
  # get函数可以根据指定的url发起网络请求
  # get函数会返回一个响应对象,函数会有返回值的。
  response = requests.get(url=main_url)  # response就是响应对象,响应数据就是在响应对象里放着。
  
  # 设置响应对象的编码格式(处理中文乱码),可以尝试设置常规编码格式,也可以打开该网页找到它的编码格式:右键->检查(F12)->元素-><head>-><meta charset='utf-8'>
  response.encoding = 'utf-8'  # 解决网页显示乱码的问题
  # 搜索:charset
    
  # 3.获取响应数据
  page_text = response.text  # text是可以返回字符串形式的响应数据/爬取到的数据
  
  # 4.持久化存储
  with open('dongfang.html', 'w', encoding='utf-8') as fp:  # encoding='utf-8'解决UnicodeEncodeError: 'gbk' codec can't encode character '\ufeff' in position 0: illegal multibyte sequence报错问题
      fp.write(page_text)

爬取51游戏中任何游戏对应的搜索结果页面数据(get请求参数处理)

url:https://www.51.com/

import requests
​
# 1.指定url
# url 中,?后面的内容就是“请求参数”。如q=传奇,请求参数是什么,爬取的内容就是什么。
# 处理请求参数,一般会创建一个字典,在字典当中放上请求参数,以键值对形式如:
game_title = input('enter a game title:')
param = {
    'q':'game_title'
}   # 字典是动态的,用于封装请求参数,字典中的value值是取决于你输入的游戏名称,网址中的请求参数可以不带了。 由原来的url = ' url = 'https://game.51.com/search/action/game/?q=传奇'改为:url = 'https://game.51.com/search/action/game/'
# 如果想设置动态请求参数(尤其是请求参数有很多个时就很有必要),就可以把所有的请求参数封装在字典当中,并且在发请求时候通过params参数带上这个字典:params=param
# 如果请求参数只有一个时,可以把参数直接拼接在url里(代码见下一个优化代码示例)
url = 'https://game.51.com/search/action/game/'
​
# 2.发起请求(携带请求参数)
# get是基于指定的url和携带了固定的请求参数进行请求发送
response = requests.get(url=url,params=param)  # params是get请求参数中的第二个参数(第一个是网址参数url),意思是使用get指定网址发请求带上怎样的请求参数,这个请求参数就是上面被封装成规定的字典格式。response是接收响应对象的变量。
​
# 3.获取响应数据
page_text = response.text # text表示获取字符串形式的响应数据
# print(page_text)
​
# 4.持久化存储
file_name = game_title + '.html'
with open(file_name,'w', encoding='utf-8') as fp:  # 注意这一定要加encoding='UTF-8',否则会出现gbk报错。
    fp.write(page_text)

51游戏只有一个请求参数时的优化代码示例:

import requests
​
# 1.指定url
game_title = input('enter a game title:')
# 如果请求参数只有一个时,可以把参数直接拼接在url里(代码见下一个优化代码示例)
url = 'https://game.51.com/search/action/game/?q=' + game_title
​
# 2.发起请求(携带请求参数)
response = requests.get(url=url)  
# 3.获取响应数据
page_text = response.text 
# print(page_text)
​
# 4.持久化存储
file_name = game_title + '.html'
with open(file_name,'w', encoding='utf-8') as fp:  
    fp.write(page_text)

中国人事考试网(UA检测)

  • url:http://www.cpta.com.cn/

    • 爬虫模拟浏览器主要是模拟请求参数和主要的请求头。

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

        • 使用浏览器发请求,则请求载体就是浏览器

        • 使用爬虫程序发请求,则请求载体就是爬虫程序

    • 反爬机制:UA检测(是最常见的请求机制),尽可能每段爬虫程序都要加UA伪装的操作,都要带上headers参数。

    • 网站后台会检测请求的载体是不是浏览器,如果是则返回正常数据,不是则返回错误数据。

    • 反反爬机制:UA伪装

    • 将爬虫发起请求的User-Agent伪装成浏览器的身份。

  • # 中国人事考试网
    import requests
    ​
    url = 'http://www.cpta.com.cn/'
    # 1.指定url
    # User-Agent:请求载体(浏览器,爬虫程序)的身份表示
    ​
    # 2.发起请求(携带请求参数)
    # # User-Agent:请求载体(浏览器,爬虫程序)的身份标识
    header = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
    }
    # 伪装了浏览器的请求头
    response = requests.get(url=url,headers=header)
    ​
    # 3.获取响应数据
    page_text = response.text
    # print(page_text)
    ​
    4.持久化存储
    with open('kaoshi.html','w') as fp:
        fp.write(page_text)
    ​
    # 程序模拟浏览器的力度不够
    • 打开抓包工具(网络(Network)),模拟浏览器发送请求,查看浏览器发送请求的细节。刷新网址抓数据包,有很多数据包,找浏览器地址栏url所对应的数据包,点击这个数据包,在Headers中可以看到Request URL:浏览器发请求所对应的url和地址栏里的url一模一样;Request Method:浏览器发请求所对应的请求方式(方法);Response(响应):请求到的数据(响应对象的数据/爬到的数据)。

    • 浏览期发送请求带了很多东西,有url、请求头、请求方式、响应头等等。所以爬虫程序模拟浏览器发送请求时就不能只带url,要尽可能携带必要的请求参数。最主要的是观察请求头,因为请求是由客户端发送的,爬虫程序是充当客户端。其中User-Agent是最常用的必要请求参数,表示请求载体(浏览器、爬虫程序)的身份标识。相当于是浏览器的身份证(value值相当于是身份证号码)。服务器首先从这里判断是谁(浏览器还是爬虫)在请求访问。请求载体是浏览器,一定可以拿到正常结果的,如果是爬虫程序,就不给结果。

中国人事考试网---站内搜索(post请求+请求参数)

  • import requests
    url = 'http://www.cpta.com.cn/category/search'
    param = {
          "keywords": "人力资源",  # 在搜索框中输入不同的关键词然后在Payload(载荷)中查看动态的请求参数。
          "搜 索": "搜 索"
      }
    header = {
          'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
    }
      # 发起了post请求:通过data参数携带了请求参数,而不是params
    response = requests.post(url=url,data=param,headers=header)
      
    page_text = response.text
      
    with open('renshi.html','w') as fp:
          fp.write(page_text)
    ​
      # 通过抓包工具定位了指定的数据包:
      # 重点要提取:url,请求方式,请求参数,请求头信息
  • 优化中国人事考试网---站内搜索(post请求+请求参数)

  • 中国人事考试网---站内搜索(post请求+请求参数)
    url = 'http://www.cpta.com.cn/category/search'
    ​
    search_title = input('enter a search title:')
    param = {
        "keywords": search_title,  # 在搜索框中输入不同的关键词然后在Payload(载荷)中查看动态的请求参数。这次的请求参数中'keywords'是动态的(变化的)
        "搜 索": "搜 索"  # 这次的请求参数中'搜 索'是静态的(固定的)
    }
    header = {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
    }
    # 发起了post请求:通过data参数携带了请求参数,而不是params
    response = requests.post(url=url, data=param, headers=header)
    ​
    page_text = response.text
    ​
    with open(search_title + '.html', 'w', encoding='utf-8') as fp:
        fp.write(page_text)
    ​
  • 小试牛刀:

    # 下厨房网站中任意菜谱搜索结果数据爬取
    # 1.指定url
    url = 'https://www.xiachufang.com/search/'
    menu_title = input('enter a search title:')
    ​
    param = {
        'keyword': menu_title,
        'cat': '1001',
        'page': '1'
    }
    # 2.发起请求(携带请求参数)
    ​
    header = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 '
                      'Safari/537.36'
    }
    ​
    response = requests.get(url=url, params=param, headers=header)
    # 处理乱码
    # response.encoding = 'utf-8'  # 或gbk,试试,一般不需要,出现乱码时在此加上这行代码。搜索:charset
    # 3.获取响应数据
    page_text = response.text
    # print(page_text)
    ​
    # 4.持久化存储
    import os
    # 获取当前脚本的绝对路径
    current_path = os.path.abspath(__file__)
    # 获取上一级的上一级目录
    parent_directory = os.path.dirname(os.path.dirname(current_path))  # os.path.dirname(os.path.dirname(current_path))相当于'../'
    ​
    # 创建文件夹
    new_directory = os.path.join(parent_directory, 'pydirectory')  # pydirectory文件夹名
    ​
    """
    在当前脚本所在位置创建文件夹可以写下面代码:
    directory = '../pydirectory/'
    检查目录是否存在,如果不存在则创建它,如果目录已存在,则不会进行任何操作。
    if not os.path.exists(directory):
        os.makedirs(directory)  # 使用 os.makedirs() 函数来创建目录
    file_path = os.path.join(directory, menu_title + '.html')  # 使用 os.path.join() 函数来拼接文件路径。这样可以确保目录存在,并且文件可以正确地保存在指定的路径中。
    with open(file_path, 'w', encoding='utf-8') as fp:
        fp.write(page_text)
    """
    if not os.path.exists(new_directory):
        os.makedirs(new_directory, exist_ok=True)  # 使用 os.makedirs() 函数来创建目录
    ​
    ​
    file_path = os.path.join(new_directory, menu_title + '.html')  # 使用 os.path.join() 函数来拼接文件路径。这样可以确保目录存在,并且文件可以正确地保存在指定的路径中。
    with open(file_path, 'w', encoding='utf-8') as fp:
        fp.write(page_text)
    ​
    print('文件已创建并写入成功!')
  • 智慧职教(动态加载数据爬取)(重点)

  • 测试:直接使用浏览器地址栏中的url,进行请求发送查看是否可以爬取到页面详情数据?

    • 不用写程序,基于抓包工具测试观察即可。

    • 经过测试发现,我们爬取到的数据并没有包含页面详情数据,why?

    • 动态加载数据:

      • 在一个网页中看到的数据,并不一定是通过浏览器地址栏中的url发起请求请求到的。如果请求不到,一定是基于其他的请求请求到的数据。

      • 动态加载数据指的就是:

        • 不是直接通过浏览器地址栏的url请求到的数据,这些数据叫做动态加载数据。

      • 如何获取动态加载数据?

        • 确定动态加载的数据是基于哪一个数据包请求到的?

        • 数据包数据的全局搜索:

          • 点击抓包工具中任何一个数据包

          • control+f进行全局搜索(弹出全局搜索框)

            • 目的:定位动态加载数据是在哪一个数据包中

          • 定位到动态加载数据对应的数据包,模拟该数据包进行请求发送即可:

            • 从数据包中提取出:

              • url

              • 请求参数 可以百度中文字体乱码编码格式

      注意:请求头中需要携带Referer。(体现模拟浏览器的力度),加大请求头的伪装。

    • import requests
      ​
      # 1.指定url
      url = 'https://www.icve.com.cn/portal/course/getNewCourseInfo'
      ​
      # 2.发起请求(携带请求参数)
      data = {
          'kczy': '',
          'order': '',
          'printstate': '',
          'keyvalue': ''
      }
      header = {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ',
          'Referer': 'https://www.icve.com.cn/portal_new/course/course.html',
          'Cookie': '_abfpc=e1d27619b723af2190d2acfdb19d09c2c5d527f6_2.0; cna=fbb17edf8286da8bcc040087eab9a769; aliyungf_tc=e1cb59b1b45263b08e6ccfd18e4b62c0a2f65b7d6868bb002a2e15270fea8c0f; acw_tc=0bc504d516975491211646908e1851f23c96e644c251c0e9dfb8cdc68ee099; verifycode=9FA4A4193EE357EF81C5469AC30116B6@638331755430200970'
      }
      response = requests.post(url, data=data, headers=header)
      ​
      # 3.获取响应数据
      # json()可以直接将请求到的响应数据进行反序列化,就不需要用正则。
      page_text = response.json()  # json()可以直接将响应对象中的响应数据由字符串反序列化成字典或列表。page_text就是字典。
      # print(page_text)
      ​
      # 4.持久化存储
      with open('teacher.txt', 'w', encoding='utf-8') as fild:
          for dic in page_text['list']:
              Teachername = dic['TeacherDisplayname']
              UnitName = dic['UnitName']
              Title = dic['Title']
              fild.write('主讲人:' + Teachername + '\n')
              fild.write('所属院校:' + UnitName + '\n')
              fild.write('专业:' + Title + '\n')
              fild.write('-----------------------' + '\n')

     

  • 肯德基(POST请求、动态加载数据、UA检测)

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

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

      • import requests
        ​
        # 肯德基地址查询
        # 1.指定url
        url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
        ​
        # 2.模拟浏览器请求头,定义请求参数,(伪装)
        location = input('enter a place name:')
        header = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            'Referer': 'http://www.kfc.com.cn/kfccda/storelist/index.aspx'
        }
        params = {
            'cname': '',
            'pid': '',
            'keyword': location,
            'pageSize': 10,
        }
        ​
        results = []
        ​
        # 3.发起循环请求多页数据并解析数据
        for page in range(1, 25):
            params['pageIndex'] = page
        ​
            response = requests.post(url=url, data=params, headers=header)
            my_data = response.json()  # 反序列化
        ​
            # 根据JSON数据的结构,使用合适的方法提取所需的数据
            # SON数据中有一个名为"Table1"的数组,每个元素包含"cityName"、"storeName"、"addressDetail"、"pro"字段
            page_results = my_data["Table1"]
            results.extend(page_results)  # 使用extend()方法将每页的结果page_results追加到results列表中
        ​
            for result in results:
                cityName = result["cityName"]
                storeName = result["storeName"]
                addressDetail = result["addressDetail"]
                pro = result["pro"]
        ​
        # 4.持久化存储
        with open(location + "肯德基地址.txt", "w", encoding="utf-8") as file:
            for result in results:
                cityName = result["cityName"]if result["cityName"] else ""
                storeName = result["storeName"]if result["storeName"] else ""
                addressDetail = result["addressDetail"]if result["addressDetail"] else ""
                pro = result["pro"]if result["pro"] else ""  # 使用条件语句来检查每个字段是否为None,如果是,则将其赋值为空字符串。这样可以避免将None与字符串进行连接操作,从而避免产生TypeError错误。
        ​
                file.write("城市: " + cityName + "\n")
                file.write("区域: " + storeName + "\n")
                file.write("地址: " + addressDetail + "\n")
                file.write("服务: " + pro + "\n")
                file.write("-----------------\n")
        ​
  • 图片数据爬取

    • # 方式1:
      import requests
      url = 'https://img2.baidu.com/it/u=862146405,1119369613&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500'
      ​
      header = {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
      }
      ​
      # 定义请求参数
      response = requests.get(url, headers=header)
      img_data = response.content  # content获取二进制形式的响应数据
      ​
      ​
      # 持久化存储
      with open("girl.jpg", "wb") as file:
              file.write(img_data)
      ​
    • # 方式2
      from urllib.request import urlretrieve
      ​
      # 图片地址
      img_url = 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201802%2F08%2F20180208154334_yTXYQ.jpeg&refer=http%3A%2F%2Fb-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1700152332&t=b691e3291e4995ff0f90d05a5e95dbd5'
      # 参数1:图片地址
      # 参数2:图片存储路径
      # urlretrieve可以根据图片地址将图片数据请求到直接存储到参数2表示的图片存储路径中
      urlretrieve(img_url, 'girl1.jpg')
    • 爬取图片的时候需要做UA伪装使用方式1,否则使用方式2

 

posted @ 2023-10-30 20:48  氨糖  阅读(15)  评论(0编辑  收藏  举报