Python 爬虫从入门到入坑全系列教程(详细教程 + 各种实战)

发布于:2022-12-11 ⋅ 阅读:(568) ⋅ 点赞:(0)

爬虫准备工作

  • 参考资料

  • python 网络数据采集 ’ 图灵工业出版

  • 精通 python 爬虫框架 Scrapy ’ 人民邮电出版社

  • python3 网络爬虫

  • Scrapy官方教程

  • 前提知识

  • url

  • http 协议

  • web 前端 ’ html, css, js

  • ajax

  • re, xpath

  • xml

爬虫简介

  • 爬虫定义:网络爬虫(又被称为网页蜘蛛、网络机器人,在 FOAF 社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫

  • 两大特征

  • 能按作者要求下载数据或者内容

  • 能自动在网络上流窜

  • 三大步骤

  • 下载网页

  • 提取正确的信息

  • 根据一定规则自动跳到另外的网页上执行上两步内容

  • 爬虫分类

  • 通用爬虫

  • 专用爬虫(聚焦爬虫)

  • Python 网络包简介

  • python2.x:urllib,urllib2,urllib3,httplib,httplib2,requests

  • python3.x:urllib,urllib3,httplib2,requests

  • python2:urllib 和 urllib2 配合使用,或者 requests

  • python3:urllib,requests

urllib

  • 包含模块

  • urllib.request: 打开和读取 urls

  • urllib.error: 包含 urllib.request 产生的常见的错误,使用 try 捕捉

  • urllib.parse: 包含解析 url 的方法

  • urllib.robotparse: 解析 robots.txt 文件

  • 案例 v01

  '''
  案例v01
  使用urllib.request请求一个网页内容,并把内容打印出来
  '''
  from urllib import request
  
  if __name__ == '__main__':
      url = "https://www.zhaopin.com/taiyuan/"
      # 打开相应url并把相应页面作为返回
      rsp = request.urlopen(url)
  
      # 把返回的结果读取出来
      # 读取出来内容类型为bytes
      html = rsp.read()
      print(type(html))
  
      # 如果想把bytes内容转换成字符串,需要解码
      print(html.decode())
  • 网页编码问题解决

  • chardet 可以自动检测页面文件的编码格式,但是,可能有误

  • 需要安装,conda install chardet

  • 案例 v02

  '''
  案例v02
  利用request下载页面
  自动检测页面编码
  
  '''
  import urllib
  import chardet
  
  if __name__ == '__main__':
      url = "http://stock.eastmoney.com/news/1407,20170807763593890.html"
  
      rsp = urllib.request.urlopen(url)
  
      html = rsp.read()
  
      # 利用chardet自动检测
      cs = chardet.detect(html)
      print(type(cs))
      print(cs)
  
      # 使用get取值保证不会出错
      html = html.decode(cs.get("encoding", "utf-8"))
      print(html)
  • urlopen 的返回对象

  • 案例 v03

    # 案例v03
    import urllib
  import chardet
  
  if __name__ == '__main__':
      url = "http://stock.eastmoney.com/news/1407,20170807763593890.html"
  
      rsp = urllib.request.urlopen(url)
  
      print(type(rsp))
      print(rsp)
  
      print("URL: {0}".format(rsp.geturl()))
      print("Info: {0}".format(rsp.info()))
      print("Code: {0}".format(rsp.getcode()))
  
      html = rsp.read()
  
      # 使用get取值保证不会出错
      html = html.decode()
- geturl: 返回请求对象的url
- info:请求反馈对象的meta信息
- getcode:返回http code
  • request.data 的使用

  • 访问网络的两种方法

  • get:

  • 利用参数给服务器传递信息

  • 参数为 dict,然后用 parse 编码

  • 案例 v04

            # 案例v04
            from urllib import request, parse

      '''
      掌握对url进行参数编码的方法
      需要使用parse模块 
      '''
      
      if __name__ == '__main__':
          url = "http://www.baidu.com/s?"
          wd = input("Input your keyword: ")
      
          # 要想使用data,需要使用字典结构
          qs = {
              "wd": wd
          }
      
          # 转换url编码
          qs = parse.urlencode(qs)
          print(qs)
      
          fullurl =url + qs
          print(fullurl)
      
          # 如果直接用可读的带参数的url,是不能访问的
          # fullurl = "http://www.baidu.com/s?wd=大熊猫"
      
          rsp = request.urlopen(fullurl)
      
          html = rsp.read()
      
          # 使用get取值保证不会出错
          html = html.decode()
      
          print(html)
    - post
        - 一般向服务器传递参数使用
        - post是把信息自动加密处理
        - 如果想使用post信息,需要用data参数
        - 使用post,意味着http的请求有可能需要更改:
            - Content-Type:application/x-www.form-urlencode
            - Content-Length: 数据长度
            - 简而言之,一旦更改请求方法,请注意其他请求头部信息相适应
        - urllib.parse.urlencode可以将字符串自动转换成上面的格式
        - 案例v05
            '''
            案例v05
      利用parse模块模拟post请求
      分析百度词典
      分析步骤:
      1. 打开F12
      2. 尝试输入单词girl,发现每次敲一个字母后都有请求
      3. 请求地址是 https://fanyi.baidu.com/sug
      4. 利用 Network-All-Headers 查看,发现FormData的值是 kw:girl
      5. 检查返回内容格式,发现返回的是json格式内容==>需要用到json包
      '''
      
      from urllib import request, parse
      # 负责处理json格式的模块
      import json
      
      '''
      大致流程是:
      1. 利用data构造内容,然后urlopen打开
      2. 返回一个json格式的结果
      3. 结果就应该是girl的释义
      '''
      
      baseurl = 'https://fanyi.baidu.com/sug'
      
      # 存放用来模拟form的数据一定是dict格式
      data = {
          # girl是翻译输入的英文内容,应该是由用户输入,此时使用硬编码
          'kw': 'girl'
      }
      
      # 需要使用parse模块对data进行编码
      data = parse.urlencode(data).encode('utf-8')
      print(type(data))
      
      # 我们需要构造一个请求头,请求头部应该至少包含传入的数据的长度
      # request要求传入的请求头是一个dict格式
      
      headers = {
          # 因为使用post请求,至少应该包含content-length 字段
          'Content-Length':len(data)
      }
      
      # 有了headers,data,url,就可以尝试发出请求了
      
      rsp = request.urlopen(baseurl, data=data)
      
      json_data = rsp.read().decode('utf-8')
      print(type(json_data))
      print(json_data)
      
      # 把json字符串转化成字典
      json_data = json.loads(json_data)
      print(type(json_data))
      print(json_data)
      
      for item in json_data['data']:
          print(item['v'], "--", item['v'])
           <class 'bytes'>
      <class 'str'>
      {"errno":0,"data":[{"k":"girl","v":"n. \u5973\u5b69; \u59d1\u5a18; \u5973\u513f; \u5e74\u8f7b\u5973\u5b50; \u5973\u90ce;"},{"k":"girls","v":"n. \u5973\u5b69; \u59d1\u5a18; \u5973\u513f; \u5e74\u8f7b\u5973\u5b50; \u5973\u90ce;  girl\u7684\u590d\u6570;"},{"k":"girlfriend","v":"n. \u5973\u670b\u53cb; \u5973\u60c5\u4eba; (\u5973\u5b50\u7684)\u5973\u4f34\uff0c\u5973\u53cb;"},{"k":"girl friend","v":" \u672a\u5a5a\u59bb; \u5973\u6027\u670b\u53cb;"},{"k":"Girls' Generation","v":" \u5c11\u5973\u65f6\u4ee3\uff08\u97e9\u56fdSM\u5a31\u4e50\u6709\u9650\u516c\u53f8\u4e8e2007\u5e74\u63a8\u51fa\u7684\u4e5d\u540d\u5973\u5b50\u5c11\u5973\u7ec4\u5408\uff09;"}]}
      <class 'dict'>
      {'errno': 0, 'data': [{'k': 'girl', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;'}, {'k': 'girls', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数;'}, {'k': 'girlfriend', 'v': 'n. 女朋友; 女情人; (女子的)女伴,女友;'}, {'k': 'girl friend', 'v': ' 未婚妻; 女性朋友;'}, {'k': "Girls' Generation", 'v': ' 少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合);'}]}
      n. 女孩; 姑娘; 女儿; 年轻女子; 女郎; -- n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;
      n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数; -- n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数;
      n. 女朋友; 女情人; (女子的)女伴,女友; -- n. 女朋友; 女情人; (女子的)女伴,女友;
       未婚妻; 女性朋友; --  未婚妻; 女性朋友;
       少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合); --  少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合);
        - 为了更多的设置请求信息,单纯的通过urlopen函数不太好用
        - 需要利用request.Request 类
        - 案例v06
            '''
            案例v06
      任务要求和内容跟v05一样
      本案例只是利用Request来实现v05的内容
      
      利用parse模块模拟post请求
      分析百度词典
      分析步骤:
      1. 打开F12
      2. 尝试输入单词girl,发现每次敲一个字母后都有请求
      3. 请求地址是 https://fanyi.baidu.com/sug
      4. 利用 Network-All-Headers 查看,发现FormData的值是 kw:girl
      5. 检查返回内容格式,发现返回的是json格式内容==>需要用到json包
      '''
      
      from urllib import request, parse
      # 负责处理json格式的模块
      import json
      
      '''
      大致流程是:
      1. 利用data构造内容,然后urlopen打开
      2. 返回一个json格式的结果
      3. 结果就应该是girl的释义
      '''
      
      baseurl = 'https://fanyi.baidu.com/sug'
      
      # 存放用来模拟form的数据一定是dict格式
      data = {
          # girl是翻译输入的英文内容,应该是由用户输入,此时使用硬编码
          'kw': 'girl'
      }
      
      # 需要使用parse模块对data进行编码
      data = parse.urlencode(data).encode('utf-8')
      
      # 我们需要构造一个请求头,请求头部应该至少包含传入的数据的长度
      # request要求传入的请求头是一个dict格式
      
      headers = {
          # 因为使用post请求,至少应该包含content-length 字段
          'Content-Length':len(data)
      }
      
      # 构造一个Request的实例
      
      req = request.Request(url=baseurl, data=data, headers=headers)
      
      # 因为已经构造了一个Request的请求实例,则所有的请求信息都可以封装在Request实例中
      rsp = request.urlopen(req)
      
      json_data = rsp.read().decode('utf-8')
      print(type(json_data))
      print(json_data)
      
      # 把json字符串转化成字典
      json_data = json.loads(json_data)
      print(type(json_data))
      print(json_data)
      
      for item in json_data['data']:
          print(item['v'], "--", item['v'])
           <class 'str'>
      {"errno":0,"data":[{"k":"girl","v":"n. \u5973\u5b69; \u59d1\u5a18; \u5973\u513f; \u5e74\u8f7b\u5973\u5b50; \u5973\u90ce;"},{"k":"girls","v":"n. \u5973\u5b69; \u59d1\u5a18; \u5973\u513f; \u5e74\u8f7b\u5973\u5b50; \u5973\u90ce;  girl\u7684\u590d\u6570;"},{"k":"girlfriend","v":"n. \u5973\u670b\u53cb; \u5973\u60c5\u4eba; (\u5973\u5b50\u7684)\u5973\u4f34\uff0c\u5973\u53cb;"},{"k":"girl friend","v":" \u672a\u5a5a\u59bb; \u5973\u6027\u670b\u53cb;"},{"k":"Girls' Generation","v":" \u5c11\u5973\u65f6\u4ee3\uff08\u97e9\u56fdSM\u5a31\u4e50\u6709\u9650\u516c\u53f8\u4e8e2007\u5e74\u63a8\u51fa\u7684\u4e5d\u540d\u5973\u5b50\u5c11\u5973\u7ec4\u5408\uff09;"}]}
      <class 'dict'>
      {'errno': 0, 'data': [{'k': 'girl', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;'}, {'k': 'girls', 'v': 'n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数;'}, {'k': 'girlfriend', 'v': 'n. 女朋友; 女情人; (女子的)女伴,女友;'}, {'k': 'girl friend', 'v': ' 未婚妻; 女性朋友;'}, {'k': "Girls' Generation", 'v': ' 少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合);'}]}
      n. 女孩; 姑娘; 女儿; 年轻女子; 女郎; -- n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;
      n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数; -- n. 女孩; 姑娘; 女儿; 年轻女子; 女郎;  girl的复数;
      n. 女朋友; 女情人; (女子的)女伴,女友; -- n. 女朋友; 女情人; (女子的)女伴,女友;
       未婚妻; 女性朋友; --  未婚妻; 女性朋友;
       少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合); --  少女时代(韩国SM娱乐有限公司于2007年推出的九名女子少女组合);