爬虫 - 基本入门

爬虫概念

  • 什么是爬虫?

    • 程序猿:写程序,然后去互联网上抓取数据的过程
    • 互联网:网,有好多的 a 链接组成的,网的节点就是每一个 a 链接 url(统一资源定位符)
  • 哪些语言可以实现爬虫?

    1. php,可以做,号称世界上最优美的语言,多进程、多线程支持的不好
    2. java,也可以做爬虫,人家做的很好,最主要的竞争对手,代码臃肿,重构成本大
    3. c、c++,是你能力的体现,不是良好的选择
    4. python,世界上最美丽的语言,语法简单、代码优美,学习成本低,支持的模块多,非常强大的框架 scrapy
  • 通用爬虫、聚焦爬虫

    • 通用爬虫:百度、360、搜狐、谷歌、必应......
    • 原理:
      1. 抓取网页
      2. 采集数据
      3. 数据处理
      4. 提供检索服务
    • 爬虫:baiduspider
    • 通用爬虫如何抓取新网站?
      1. 主动提交 url
      2. 设置友情链接
      3. 百度会和 DNS 服务商合作,抓取新网站
    • 检索排名
      • 竞价排名
      • 根据 pagerank 值、访问量、点击量(SEO)
    • robots.txt
      • 如果不想让百度爬取,可以编写 robots.txt,这个协议是口头上的协议
      • 自己写的爬虫程序不需要遵从
    • 聚焦爬虫
      • 根据特定的需求,抓取指定的数据
      • 思路?
        • 代替浏览器上网。网页的特点:
          1. 网页都有自己唯一的 url
          2. 网页内容都是 html 结构的
          3. 使用的都是 http、https 协议
      • 爬取步骤:
        1. 给一个 url
        2. 写程序,模拟浏览器访问 url
        3. 解析内容,提取数据
  • 环境

    • windows 环境、linux 环境
    • Python3.6 64 位的
    • sublime、pycharm、vscode
  • 学习内容

    1. 使用到的库:urllib、requests、bs4......
    2. 解析网页内容的知识:正则表达式、bs4、xpath、jsonpath
    3. 涉及到动态 html:selenium+phantomjs、chromeheadless
    4. scrapy 框架:高性能框架使用
    5. scrapy-redis 组件:redis,分布式爬虫
    6. 涉及到爬虫 - 反爬虫 - 反反爬虫的一些内容:UA、代理、验证码、动态页面等

HTTP 协议

  • 什么是协议?

    双方规定的传输形式

  • 端口 (常见端口)

    http(80)、https(443)、ssh(22)、mysql(3306)、redis(6379)、mongo(27017)、ftp(21)

  • HTTP 协议:应用层的协议

    • 什么是 HTTP 协议?

      • HTTP 是 Hyper Text Transfer Protocol(超文本传输协议)的缩写。它的发展是万维网协会(World Wide Web Consortium)和 Internet 工作小组 IETF(Internet Engineering Task Force)合作的结果,(他们)最终发布了一系列的 RFC,RFC 1945 定义了 HTTP/1.0 版本。其中最著名的就是 RFC 2616。RFC 2616 定义了今天普遍使用的一个版本 ——HTTP 1.1。
      • HTTP 协议(HyperText Transfer Protocol,超文本传输协议)是用于从 WWW 服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示 (如文本先于图形) 等。
    • HTTP 响应模型

      HTTP 协议永远都是客户端发起请求,服务器回送响应。见下图所示:

      网站原理

      这样就限制了使用 HTTP 协议,无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端。

    • 工作流程

      一次 HTTP 操作称为一个事务,其工作过程可分为四步:

      1. 首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP 的工作开始。
      2. 建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源定位符(URL)、协议版本号,后边是 MIME 信息包括请求修饰符、客户机信息和可能的内容。
      3. 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是 MIME 信息包括服务器信息、实体信息和可能的内容。
      4. 客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。

      如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,有显示屏输出。对于用户来说,这些过程是由 HTTP 自己完成的,用户只要用鼠标点击,等待信息显示就可以了。

    • 参考链接;参考书籍:《图解 HTTP 协议》

    • 网站原理

      网站原理

  • HTTP 请求

    • HTTP 请求组成

      一个 HTTP 请求报文由请求行(request line)、请求头部(headers)、空行(blank line)和请求数据(request body)4 个部分组成。

    • 请求行

      请求行分为三个部分:请求方法、请求地址 URL 和 HTTP 协议版本,它们之间用空格分割。例如,GET /index.html HTTP/1.1

      • HTTP 请求方法
        序号 方法 描述
        1 GET 请求指定的页面信息,并返回实体主体。
        2 HEAD 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
        3 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和 / 或已有资源的修改。
        4 PUT 从客户端向服务器传送的数据取代指定的文档的内容。
        5 DELETE 请求服务器删除指定的页面。
        6 CONNECT HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
        7 OPTIONS 允许客户端查看服务器的性能。
        8 TRACE 回显服务器收到的请求,主要用于测试或诊断。
        9 PATCH 是对 PUT 方法的补充,用来对已知资源进行局部更新 。

        HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。
        HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。

      • 请求地址 URL
        • URL (uniform resource locator):统一资源定位符,是一种资源位置的抽象唯一识别方法。
        • 组成:<协议>://< 主机 >:< 端口 >/< 路径 >

          端口和路径有时可以省略(HTTP 默认端口号是 80)

        • 图示
    • 协议

      协议版本的格式为:HTTP / 主版本号。次版本号,常用的有 HTTP/1.0 和 HTTP/1.1

    • 常见请求头

      • accept:浏览器通过这个头告诉服务器,它所支持的数据类型
      • Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
      • Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式
      • Accept-Language:浏览器通过这个头告诉服务器,它的语言环境
      • Host:浏览器通过这个头告诉服务器,想访问哪台主机
      • If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间
      • Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的 防盗链
      • Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是何持链接
      • X-Requested-With: XMLHttpRequest 代表通过 ajax 方式进行访问
  • HTTP 响应

    HTTP 响应报文由状态行(status line)、相应头部(headers)、空行(blank line)和响应数据(response body)4 个部分组成。

    • 状态行

      状态行由 3 部分组成,分别为:协议版本、状态码、状态码扫描。其中协议版本与请求报文一致,状态码描述是对状态码的简单描述。

      • HTTP 状态码分类
        分类 分类描述
        1** 信息,服务器收到请求,需要请求者继续执行操作
        2** 成功,操作被成功接收并处理
        3** 重定向,需要进一步的操作以完成请求
        4** 客户端错误,请求包含语法错误或无法完成请求
        5** 服务器错误,服务器在处理请求的过程中发生了错误
      • HTTP 状态码列表
        状态码 状态码英文名称 中文描述
        100 Continue 继续。客户端应继续其请求
        101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到 HTTP 的新版本协议
        200 OK 请求成功。一般用于 GET 与 POST 请求
        201 Created 已创建。成功请求并创建了新的资源
        202 Accepted 已接受。已经接受请求,但未处理完成
        203 Non-Authoritative Information 非授权信息。请求成功。但返回的 meta 信息不在原始的服务器,而是一个副本
        204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
        205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
        206 Partial Content 部分内容。服务器成功处理了部分 GET 请求
        300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
        301 Moved Permanently 永久移动。请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到新 URI。今后任何新的请求都应使用新的 URI 代替
        302 Found 临时移动。与 301 类似。但资源只是临时被移动。客户端应继续使用原有 URI
        303 See Other 查看其它地址。与 301 类似。使用 GET 和 POST 请求查看
        304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
        305 Use Proxy 使用代理。所请求的资源必须通过代理访问
        306 Unused 已经被废弃的 HTTP 状态码
        307 Temporary Redirect 临时重定向。与 302 类似。使用 GET 请求重定向
        400 Bad Request 客户端请求的语法错误,服务器无法理解
        401 Unauthorized 请求要求用户的身份认证
        402 Payment Required 保留,将来使用
        403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
        404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置 "您所请求的资源无法找到" 的个性页面
        405 Method Not Allowed 客户端请求中的方法被禁止
        406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求
        407 Proxy Authentication Required 请求要求代理的身份认证,与 401 类似,但请求者应当使用代理进行授权
        408 Request Time-out 服务器等待客户端发送的请求时间过长,超时
        409 Conflict 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
        410 Gone 客户端请求的资源已经不存在。410 不同于 404,如果资源以前有现在被永久删除了可使用 410 代码,网站设计人员可通过 301 代码指定资源的新位置
        411 Length Required 服务器无法处理客户端发送的不带 Content-Length 的请求信息
        412 Precondition Failed 客户端请求信息的先决条件错误
        413 Request Entity Too Large 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个 Retry-After 的响应信息
        414 Request-URI Too Large 请求的 URI 过长(URI 通常为网址),服务器无法处理
        415 Unsupported Media Type 服务器无法处理请求附带的媒体格式
        416 Requested range not satisfiable 客户端请求的范围无效
        417 Expectation Failed 服务器无法满足 Expect 的请求头信息
        500 Internal Server Error 服务器内部错误,无法完成请求
        501 Not Implemented 服务器不支持请求的功能,无法完成请求
        502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
        503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中
        504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求
        505 HTTP Version not supported 服务器不支持请求的 HTTP 协议的版本,无法完成处理
    • 响应头

      • Location: 服务器通过这个头,来告诉浏览器跳到哪里
      • Server:服务器通过这个头,告诉浏览器服务器的型号
      • Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
      • Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
      • Content-Language: 服务器通过这个头,告诉浏览器语言环境
      • Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
      • Refresh:服务器通过这个头,告诉浏览器定时刷新
      • Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
      • Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
      • Expires: -1 控制浏览器不要缓存
      • Cache-Control: no-cache
      • Pragma: no-cache
    • 响应数据

      用于存放需要返回给客户端的数据信息。

  • HTTPS 的工作原理

    我们都知道 HTTPS 能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用 HTTPS 协议。

    客户端在使用 HTTPS 方式与 Web 服务器通信时有以下几个步骤,如图所示。

    1. 客户使用 https 的 URL 访问 Web 服务器,要求与 Web 服务器建立 SSL 连接。
    2. Web 服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
    3. 客户端的浏览器与 Web 服务器开始协商 SSL 连接的安全等级,也就是信息加密的等级。
    4. 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
    5. Web 服务器利用自己的私钥解密出会话密钥。
    6. Web 服务器利用会话密钥加密与客户端之间的通信。
  • HTTPS 和 HTTP 的区别主要如下:

    1. https 协议需要到 ca 申请证书,一般免费证书较少,因而需要一定费用。
    2. http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议。
    3. http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
    4. http 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全。
    5. 参考链接

一个网页的呈现,中间不止一次 http 请求,平均一个网页差不多 10-15 个 http 请求,这是我们可以使用抓包工具进行处理信息。

  • Chrome 抓包

    • 右键 -> 检查 -> network3
    • 点击请求,右边栏请求详细信息
      • 右边栏:request headers response
      • query string:get 参数
      • form data:post 参数
  • Fiddler

    1. 配置

      1
      2
      3
      4
      5
      6
      tools==>options==>https
      选中:capture https
      decrypt https trafic
      ignore xxx
      点击右边的action,信任根证书
      配置完毕,fiddler关闭重启即可
    2. 抓包

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <>:         html内容
      {json}: json数据,很有可能就是个接口
      {css}: css文件
      {js}: js文件

      停止抓取: file==》capture 点击就会切换

      点击请求,右边选中 Inspectors
      右上: http请求信息
      raw: 请求头部的详细信息
      webforms:请求所带参数,query_string formdata

      右下: http响应信息
      首先点击黄色条进行解码
      raw: 响应的所有信息
      headers:响应头
      json: 接口返回的内容

      左下黑色框,输入指令
      clear: 清除所有请求
      select json: 快速选择所有json请求
      select image:图片请求
      select html: html请求
      ?内容: 搜索包含这个内容的所有请求,敲enter执行
    3. Fiddler 中文版

    4. 会话图标

  • Sublime

    1. 下载并安装然后使用下边注册码激活软件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      ----- BEGIN LICENSE -----
      Member J2TeaM
      Single User License
      EA7E-1011316
      D7DA350E 1B8B0760 972F8B60 F3E64036
      B9B4E234 F356F38F 0AD1E3B7 0E9C5FAD
      FA0A2ABE 25F65BD8 D51458E5 3923CE80
      87428428 79079A01 AA69F319 A1AF29A4
      A684C2DC 0B1583D4 19CBD290 217618CD
      5653E0A0 BACE3948 BB2EE45E 422D2C87
      DD9AF44B 99C49590 D2DBDEE1 75860FD2
      8C8BB2AD B2ECE5A4 EFC08AF2 25A9B864
      ------ END LICENSE ------&#8203; ;
    2. 安装插件管理器 (2 种方式):packagecontrol

      1. 指令安装:
        按 view 下面的 show console, 输入如下指令敲 enter 即可
        1
        import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())
      2. 下载 packagecontrol 包放到指定位置即可(搜教程)
    3. 安装插件

      1. 输入快捷键 ctrl+shift+p
      2. 输入 pci,选中 install packages
      3. 进来后输入想要安装的插件名字点击就能安装,如:ChineseLocalizations (汉化插件)
    4. 解决输入 pci,找不到包库的问题

      输入如下指令,敲 enter

      1
      import urllib.request,os,hashlib; h = 'eb2297e1a458f27d836c04bb0cbaf282' + 'd0e7a3098092775ccb37ca9d6b2e4b7d'; pf = 'Package Control.sublime-、package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

urllib 库

  • 简介

    • 模拟浏览器发送请求的库,Python 自带
    • Python2:urllib、urllib2
    • Python3: urllib.request 、urllib.parse
    • 字符串 ==> 字节类型之间的转化
      • encode():字符串 ==> 字节类型
        • 如果小括号里面不写参数,默认是 utf8
        • 如果写,你就写 gbk
      • decode():字节类型 ==> 字符串
        • 如果不写,默认 utf8
        • 如果写,写 gbk
    • 示例
      1
      2
      3
      4
      5
      import urllib.request

      url = 'http://www.baidu.com/'
      response = urllib.request.urlopen(url=url)
      print(response.read().decode())
  • urllib.request

    • urlopen(url)
    • urlretrieve(url, image_path)
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      import urllib.request

      # 下载图片
      image_url = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1597694880959&di=6ab8a1ca4e859d02b8c498d4f29244c8&imgtype=0&src=http%3A%2F%2Fimages.china.cn%2Fattachement%2Fjpg%2Fsite1000%2F20170424%2F7427ea210a2c1a677cda59.jpg'

      # response = urllib.request.urlopen(image_url)
      # with open('meiNv.jpg', 'wb')as fp:
      # fp.write(response.read())

      urllib.request.urlretrieve(image_url, 'mei.jpg')
  • urllib.parse

    • quote :url 编码函数,将中文进行转化为 % xxx
    • unquote:url 解码函数,将 % xxx 转化为指定字符
    • urlencode:给一个字典,将字典拼接为 query_string,并且实现了编码的功能
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      import urllib.parse

      # https://www.baidu.com/s?ie=UTF-8&wd=%E5%91%A8%E6%9D%B0%E4%BC%A6
      # url只能由特定的字符组成:字母、数字、下划线
      # 如果出现其他的,比如:$、空格、中文等,就要对其进行编码

      url = 'https://www.baidu.com/s?ie=UTF-8&wd=周杰伦'
      ret = urllib.parse.quote(url)
      print(ret)
      re = urllib.parse.unquote(ret)
      print(re)
      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
      import urllib.parse

      url = 'http://www.baidu.com/index.html'

      # url = http://www.baidu.com/index.html?name=goudan&age=18&sex=nv&height=180'
      # 假如参数有 name age sex height
      name = '狗蛋'
      age = 18
      sex = '女'
      height = '180'

      data = {
      'name': name,
      'age': age,
      'sex': sex,
      'height': height,
      'weight': 180,
      }

      query_string = urllib.parse.urlencode(data)
      url = url + query_string

      # 遍历字典
      # lt = []
      # for k, v in data.items():
      # lt.append(k + '=' + str(v))
      # query_string = '&'.join(lt)
      # url = url + '?' + query_string

      print(url)
  • response

    • read() :读取响应内容,内容是字节类型
    • geturl() :获取请求的 url
    • getheaders():获取头部信息,列表里面有元组
    • getcode():获取状态码
    • readlines():按行读取,返回列表,都是字节类型
    • 示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      import urllib.request

      url = 'http://www.baidu.com/'
      response = urllib.request.urlopen(url=url)

      print(response.read())
      print(response.geturl())
      print(response.url)
      print(dict(response.getheaders()))
      print(dict(response.headers))
      print(response.getcode())
      print(response.code)
      print(response.readlines())

get 方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import urllib.request
import urllib.parse

word = input('请输入您想要搜索的内容:')
url = 'http://www.baidu.com/s?'

# 参数写成一个字典
data = {
'ie': 'UTF-8',
'wd': word,
}

query_string = urllib.parse.urlencode(data)
url += query_string

# 发送请求
response = urllib.request.urlopen(url)
file_name = word + '.html'
with open(file_name, 'wb')as fp:
fp.write(response.read())

构建请求头部信息

  • 这是反爬第一步
  • 伪装自己的 UA,让服务端认为你是浏览器在上网
  • 构建请求对象: urllib.request.Request()
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import urllib.request
    import urllib.parse

    url = 'http://www.baidu.com/'
    # response = urllib.request.urlopen(url)
    # print(response.read().decode())
    # Python默认User-Agent: Python-urllib/3.6

    # 自己要伪装的头部
    headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
    }
    # 构建请求对象
    request = urllib.request.Request(url=url, headers=headers)
    # 发送请求
    response = urllib.request.urlopen(request)
    print(response.read().decode())