头图:[https://cdn.naraku.cn/imgs/Python_requests-html0.jpg](https://cdn.naraku.cn/imgs/Python_requests-html0.jpg)

简介

Requests是模拟HTTP的测试库,玩过Python爬虫的同学一定听过或者用过,但是Requests只负责网络请求,不会对响应结果进行解析。而该库的作者后来基于现有的框架进行二次封装,又发布了一个更好用的Requests-html库用于解析HTML。
Requests-html具有以下特性

  • 完全支持 JavaScript
  • CSS、xPath 选择器
  • 模拟用户代理
  • 自动跟踪重定向
  • 连接池和 cookie 持久化

    安装

    1. pip install requests-html

    开始使用

  • 爬取博客内全部链接,此处会返回一个set集合类型的全部链接

    1. from requests_html import HTMLSession
    2. session = HTMLSession()
    3. response = session.get("https://www.naraku.cn/")
    4. # 获取页面Base_URL
    5. base_url = response.html.base_url
    6. print(base_url)
    7. # 获取网页内的所有链接
    8. links = response.html.links
    9. print(links)
    10. # 获取网页内的所有链接绝对路径形式
    11. ab_links = response.html.absolute_links
    12. print(ab_links)

    选取元素

  • 上面返回一大堆链接,杂乱且不好利用,不如单纯爬取文章标题和对应的链接吧。request-html支持CSS选择器和xPath两种语法来选取HTML元素。

    CSS选择器 - find()

  • 先用CSS选择器来看看吧

    1. from requests_html import HTMLSession
    2. session = HTMLSession()
    3. response = session.get("https://www.naraku.cn/archives.html")
    4. posts = response.html.find(".text-lt")
    5. for post in posts:
    6. print(f'{post.text}: {post.links}')

    小记 - Requests-html - 图1

    xPath选择器 - xpath()

  • 上面写的CSS语法匹配到的不是很精确,因为页面内其它地方也调用了text-lt这个类,导致前面2行出现了奇怪的东西。所以使用xPath语法来更精确地匹配一下。不熟悉此语法的可以参考xPath用法总结整理

    1. from requests_html import HTMLSession
    2. session = HTMLSession()
    3. response = session.get("https://www.naraku.cn/archives.html")
    4. posts = response.html.xpath("//a[@class='text-lt']")
    5. for post in posts:
    6. print(f'{post.text}: {post.links}')

    小记 - Requests-html - 图2

    实战

  • 这里尝试爬取一下闲读。进入页面后通过点击下方分页并观察URL,可以发现分页结构为[http://gank.io/xiandu/wow/page/{](http://gank.io/xiandu/wow/page/%7B)页面数},通过修改页面数即可实现分页。此处只爬取前2页以作演示

    1. from requests_html import HTMLSession
    2. if __name__ == '__main__':
    3. pages_dict = {} # 保存文章标题及链接
    4. session = HTMLSession()
    5. for index in range(1, 3):
    6. url = f"https://gank.io/xiandu/wow/page/{index}"
    7. response = session.get(url)
  • 由于爬取的数量过多,这里添加一个随机UA,需要引入requests_html

    • requests_html.user_agent(style=None):返回一个指定风格的合法UA,默认Chrome风格
      1. from requests_html import HTMLSession
      2. import requests_html
      3. if __name__ == '__main__':
      4. pages_dict = {} # 保存文章标题及链接
      5. session = HTMLSession()
      6. ua = requests_html.user_agent() # 随机UA
      7. headers = {'User-Agent': ua}
      8. for index in range(1, 3):
      9. url = f"https://gank.io/xiandu/wow/page/{index}"
      10. response = session.get(url, headers=headers)
  • 定义一个函数用于解析页面,并将文章的标题和链接存到字典中。这里依然使用xPath语法

    1. def parse_html(r):
    2. pages = r.html.xpath("//a[@class='site-title']")
    3. for page in pages:
    4. pages_dict[page.text] = list(page.links)[0]
  • 最后将字典写入json,此处需引入json

    1. import json
    2. def save_data():
    3. pages_json = json.dumps(pages_dict, ensure_ascii=False) # 转为json对象
    4. with open("Pages_Dic.json", "w", encoding='utf-8') as f:
    5. f.write(pages_json)
  • 完整代码

    1. # -*- coding:utf-8 -*-
    2. """
    3. @Author: Naraku
    4. @File: learn-requests-html.py
    5. @Time: 2020/2/23
    6. """
    7. from requests_html import HTMLSession
    8. import requests_html
    9. import json
    10. def parse_html(r):
    11. pages = r.html.xpath("//a[@class='site-title']")
    12. for page in pages:
    13. pages_dict[page.text] = list(page.links)[0]
    14. def save_data():
    15. pages_json = json.dumps(pages_dict, ensure_ascii=False) # 转为json对象
    16. with open("Pages_Dic.json", "w", encoding='utf-8') as f:
    17. f.write(pages_json)
    18. if __name__ == '__main__':
    19. pages_dict = {} # 用于保存文章标题及链接
    20. session = HTMLSession()
    21. ua = requests_html.user_agent() # 随机UA
    22. headers = {'User-Agent': ua}
    23. for index in range(1, 3):
    24. url = f"https://gank.io/xiandu/wow/page/{index}"
    25. response = session.get(url, headers=headers)
    26. parse_html(response)
    27. save_data() # 保存为json文件