爬虫入门到精通-爬虫之异步加载(实战花瓣网)

本文章属于爬虫入门到精通系统教程第八讲

本次我们会讲解两个知识点

  1. 异步加载
  2. headers中的Accept

本次我们要抓取的是花瓣网美女照片美女_花瓣,陪你做生活的设计师(发现、采集你喜欢的美女图片)_花瓣网

本次我们会用到的辅助包

1. scrapy/parsel (假如你用过scrapy,那么一定不陌生,这就是其中提取器)

Parsel is a library to extract data from HTML and XML using XPath and CSS selectors

简单来讲就是集成了xpath和css,只要你会xpath的话,那么用法没有什么区别

>>> from parsel import Selector
>>> sel = Selector(text=u"""<html>
        <body>
            <h1>Hello, Parsel!</h1>
            <ul>
                <li><a href="http://example.com">Link 1</a></li>
                <li><a href="http://scrapy.org">Link 2</a></li>
            </ul
        </body>
        </html>""")
>>>
>>> sel.css('h1::text').extract_first()
u'Hello, Parsel!'
>>>
>>> sel.css('h1::text').re('\w+')
[u'Hello', u'Parsel']
>>>
>>> for e in sel.css('ul > li'):
        print(e.xpath('.//a/@href').extract_first())
http://example.com
http://scrapy.org

安装方法: pip install parsel

2. scrapinghub/js2xml

Convert Javascript code to an XML document

简单来讲就是 将JavaScript代码转换为xml文档。然后可以使用xpath从JavaScript中提取数据,不用写一堆正则了。

>>> import js2xml
>>>
>>> jscode = """function factorial(n) {
...     if (n === 0) {
...         return 1;
...     }
...     return n * factorial(n - 1);
... }"""
>>> parsed = js2xml.parse(jscode)
>>>
>>> parsed.xpath("//funcdecl/@name")  # extracts function name
['factorial']
>>>
>>> print js2xml.pretty_print(parsed)  # pretty-print generated XML
<program>
  <funcdecl name="factorial">
    <parameters>
      <identifier name="n"/>
    </parameters>
    <body>
      <if>
        <predicate>
          <binaryoperation operation="===">
            <left>
              <identifier name="n"/>
            </left>
            <right>
              <number value="0"/>
            </right>
          </binaryoperation>
        </predicate>
        <then>
          <block>
            <return>
              <number value="1"/>
            </return>
          </block>
        </then>
      </if>
      <return>
        <binaryoperation operation="*">
          <left>
            <identifier name="n"/>
          </left>
          <right>
            <functioncall>
              <function>
                <identifier name="factorial"/>
              </function>
              <arguments>
                <binaryoperation operation="-">
                  <left>
                    <identifier name="n"/>
                  </left>
                  <right>
                    <number value="1"/>
                  </right>
                </binaryoperation>
              </arguments>
            </functioncall>
          </right>
        </binaryoperation>
      </return>
    </body>
  </funcdecl>
</program>

>>>

安装方法: pip install js2xml

开始爬虫

我们先打开美女_花瓣,陪你做生活的设计师(发现、采集你喜欢的美女图片)_花瓣网

页面分析

如果我们想把这里面所有美女照片抓取下来的话,那么我们的操作步骤应该是这样的

  1. 打开首页的每一个"相框",然后点进去
  1. 获取所有图片的链接,然后下载下来

程序实现:

用程序实现的话,也是挺简单的

  1. 获取首页所有“相框”的链接
  2. 点进去每个链接
  3. 获取详情页的所有图片地址
  4. 下载图片

代码:

  1. 获取首页所有“相框”的链接
    1. 我们打开美女_花瓣,陪你做生活的设计师(发现、采集你喜欢的美女图片)_花瓣网
    2. 按F12
    3. 点击如图所示的位置


    1. 点击任何一个相框,然后你会看到网页的源代码自动会跳到你当前选中的地方
    1. 然后你就可以在这附近找找你想要的链接地址(可以看到/pins/1062650100/,我们可以打开这个地址看看,确认下是我们想要找的)


      1. 可以看到图片是一样的,说明我们要找的没错
      1. 那么既然我们找到了需要的链接,接下来就是用程序定位到这了。
      2. 可以看到链接这边有个class="img x layer-view loaded",那么我们可以用xpath
      3. //a[@class="img x layer-view loaded"]/@href
来获取地址了


  1. 用代码实现:
    1. import requests
      from parsel import Selector

      url = 'huaban.com/favorite/bea'
      headers = {
      'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
      }
      z = requests.get(url,headers=headers)
      print z.status_code
      # 返回200
      # 使用parsel中的Selector 来解析
      sel = Selector(text=z.text)
      print sel.xpath('//a[@class="img x layer-view loaded"]/@href')
      # 发现返回为空
这边为什么会返回空呢?不是应该返回所有链接的么?
    1. 我们可以查看下网页源代码,可以发现的内容都是通过js渲染上去的,所以我们才获取不到内容(这个可以用js2xml来解析,先放在这里,到详情页再来处理。)
  1. 所谓的异步加载
    1. 我们还是打开美女_花瓣,陪你做生活的设计师(发现、采集你喜欢的美女图片)_花瓣网
    2. 可以发现我们把页面拖动到最下面,会自动加载出新的内容(整个页面没有跳转,这就是所谓的异步加载。有些网页是需要手动点击“加载更多的”,原理都是一样的)
    3. 获取异步加载的请求
      1. 打开F12
      2. 拖动到页面最下面(有些网站是点击加载更多)
      3. 注意 我有勾选"xhr"
      4. 可以看到每次页面到最底部,都会发送一个请求。这个请求就是所谓的异步加载请求。
      1. 可以看到请求的参数如下:
        j0ga0has:
        max:1062527343
        limit:20
        wfl:1
"j0ga0has"第一个参数不知道是怎么回事,先放着
"max"是 最上面的pin_id
limit 是每次返回的条数
有人可能会问,你怎么知道这个参数是干嘛的?其实都是试出来的(或者说看出来的)
      1. 如下图的pin_id,可以发现下一条请求的max就是上一条请求获取到的最后一个pin_id


      1. 我们查看返回值,发现竟然是json格式的,这样的话,都不需要我们解析了,那我们找找我们需要的链接地址在哪。
      1. 发现链接地址就是由pin_id拼接而成的,所以我们只要获取到这个pin_id就行。
    1. 用程序来实现:
    2. url = 'huaban.com/favorite/bea'
      params = {
      'j0ga0hbi':'',
      'max':'1062161596',
      'limit':'100',
      'wfl':'1'
      }
      z1 = requests.get(url=url,params=params,headers=headers)
      print z1.status_code
      # 返回200
      print z1.json()
      # 报错

然后发现竟然报错了。。。
为什么呢?我们查看请求的时候就是jsno格式的啊
我们打印下源代码看看

你会看到竟然是"<!DOCTYPE html><html "这样的,但是我们上面查看请求的时候,明明是如下图这样的啊

那么到底是哪里出了问题呢?
我们再次查看之前我们看到的异步请求
可以发现它有几个 特别的请求头
指定了格式为json ,那么我们加上去看看呢
Accept:application/json
X-Request:JSON
X-Requested-With:XMLHttpRequest


    1. headers1 = {
      'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
      'Accept':'application/json',
      'X-Request':'JSON',
      'X-Requested-With':'XMLHttpRequest'
      }
      z2 = requests.get(url=url,params=params,headers=headers1)
      print z2.content

可以看到返回值和我们之前一样了。
获取pin_id

最后只要把pin_id拼接成url就可以了,如果你想要爬取所有的图片的话,那么你只需要把最后的pin_id 传入给max,再请求一次,直到pins为空为止

  1. 在上面我们已经获取到了所有的详情页的地址,那么我们现在只要获取到图片链接就行
    1. 随便打开一个详情页花瓣
    2. 查看图片地址
    1. 复制图片地址到网页源代码里面找找看
      1. 全部复制,发现没有找到


      1. 那么我们复制一部分
        1. 可以看到已经找到了,这边你也可以用正则表达式,来匹配所有的地址,但是太麻烦了,我们可以用js2xml
    1. 看代码.



      1. 就这样,我们已经把图片地址获取到了,只需要拼接下即可(记得去下重)


最后再次总结一下

看完本篇文章后,你应该要

  • 能知道如何抓取异步加载的请求
  • 了解js2xml的用法
  • 了解headers的用法

最后代码都在 kimg1234/pachong


填一个坑,我在爬虫入门到精通-网页的下载 - 知乎专栏留下以下问题

@王东尧


其实解决方法也挺简单的,就是把content-type这一行注释掉。

那么为什么注释掉就可以了呢?请仔细研究研究http协议。。。



欢迎关注本人的微信公众号获取更多Python爬虫相关的内容

(可以直接搜索「写bug的高师傅」)

编辑于 01-14

文章被以下专栏收录