Python爬虫实战入门三:简单的HTML解析——爬取腾讯新闻



上一章咱们使用Python实现了一个简单的HTTP请求,瞧着简单,爬虫就是模拟人打开一个个URL浏览一个个网页来爬取数据的,一个成功的HTTP请求,就是一个爬虫的基础。
接下来,咱们以一个实际的例子:爬取百度新闻,来介绍使用BeautifulSoup对HTML进行解析处理。

爬取腾讯新闻

1、寻找数据特征
腾讯新闻的网址URL为:http://news.qq.com/
我们打开网页看看:

腾讯新闻

我们需要爬取这个页面每一条新闻的标题,鼠标右击一条新闻的标题,选择“审查元素”,出现下图的窗口:

审查元素

图片中红框的位置就是那一条新闻标题在HTML中的结构、位置和表现形式:

它上一级元素为:<em class=”f14 l24″>,再上一级元素为:<div class=”text”>
我们再看另一条新闻的标题,发现它的结构和之前我们分析的新闻标题的结构是一样的:

通过这些信息,我们就可以确定新闻标题在HTML文档中的位置。
接下来,我们开始使用Python对腾讯新闻标题进行爬取
** 2、编写爬取代码
首先上完整的代码

 

运行程序,获取到的部分结果为如下所示:

这正是我们所需要的。
虽然代码很简单,但还是做一点点讲解,方便刚刚接触的同学。
3、代码解析

首先,我们定义了文件的编码形式为UTF-8,以避免一些编码错误导致中文乱码。

然后,我们引入了相关的模块,requests用于HTTP请求,BeautifulSoup用于解析HTML响应。

设置一个变量url,值为腾讯新闻的URL

使用requests.get()对URL发起GET方式的HTTP请求,并使用text()方法获取响应的文本内容,最后将其赋值给变量wbdata。

使用BeautifulSoup对响应文本wbdata进行解析处理,这里使用的是lxml库,如何没有安装,可以使用Python自带的html.parser,效果也是一样的。

在解析后的文本中,使用select选择器,在文本中选择指定的元素,通常我们还会使用find()和findall()方法来进行元素选择。
这一步返回的为一个列表,列表内的元素为匹配的元素的HTML源码。

对结果列表进行遍历,再从遍历的元素中提取出数据,get(“href”)表示获取属性名为“href”的属性值,get_text()表示获取标签的文本信息。

这样,一个简单的腾讯新闻爬虫就完成了,如果对requests模块和BeautifulSoup模块有更加深的学习欲望,可以查看它们的官方文档:
requests官方文档(中文): http://docs.python-requests.org/zh_CN/latest/
BeautifulSoup文档(中文):
https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html

  1. Plum说道:

    博主你第一段里写成百度新闻了 :eek:

    1. zmister说道:

      失误失误,开始是选百度新闻的,后来换成爬腾讯

  2. Leo-Chen说道:

    代码第十三行的选择器中的div.text > em.f14 > a.linkto怎么得到,是不是谷歌浏览器审查copy selector,我之前看到教程也是这样,但我自己copy出来的就是“body > div.wrap > div.container > div.main > div.news > div.con > div.item.channel > div:nth-child(1) > div.content > em > a”这么个奇怪的东东,而且直接抄博主的里的div.text > em.f14 > a.linkto也会报错,这是为什么,我几乎完全小白,希望博主解答

    1. Leo-Chen说道:

      没错了,照博主代码写可以出结果,但就是还是不懂那个选择器里的div.text > em.f14 > a.linkto是怎么出来的

      1. zmister说道:

        div.text > em.f14 > a.linkto表示

        下面的下面的
    2. zmister说道:

      额,这个你需要了解一下CSS选择器的基本的概念,从调试控制台copy出来的是其对于整个页面的CSS选择器位置,我设置的那个是提取简化之后的,要看具体页面来,>符号表示html元素的下一层级关系

    3. MJ说道:

      我是python3.5:"div.text > em.f14 > a.linkto"这一块,“>”两边需要有空格.
      W3C文档中讲的是“”CSS选择器子结合符两边可以有空白符,这是可选的”

  3. 无怀氏说道:

    赞赞!

  4. LEO说道:

    为什么我爬到的都是{u'\u94fe\u63a5': 'http://news.qq.com/a/20170322/032748.htm', u'\u6807\u9898': u'\u4e2d\u65b9\u56de\u5e94\u671d\u9c9c\u53d1\u5c04\u6570\u679a\u5bfc\u5f39\uff1a\u534a\u5c9b\u5c40\u52bf\u5251\u62d4\u5f29\u5f20'} 的

    1. zmister说道:

      这个你用utf-8编码解码一下就好了

      1. 匿名说道:

        怎么解码,我的也是 {u'\u94fe\u63a5': 'http://news.qq.com/a/20170322/032748.htm', u'\u6807\u9898': u'\u4e2d\u65b9\u56de\u5e94\u671d\u9c9c\u53d1\u5c04\u6570\u679a\u5bfc\u5f39\uff1a\u534a\u5c9b\u5c40\u52bf\u5251\u62d4\u5f29\u5f20'}

  5. 杯具也悲剧说道:

    参考这篇加百度正则爬了一个网站的动漫图,谢谢指导

  6. kaze说道:

    为什么我跑出来会报错,提示:
    bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?

  7. kaze说道:

    博主,“工具准备”篇中把安装lxml库加上吧,实测得先pip install lxml,不然按照本文方法写的代码跑不了

    1. zmister说道:

      哈哈,失误失误,多谢指正

  8. 菜鸟一枚说道:

    请问各位大师,这个程序为什么没有反应?还望赐教指点,小弟拜谢
    >>> # coding:utf-8
    ... import requests
    >>> from bs4 import BeautifulSoup
    >>>
    >>> url='http://new.qq.com/'
    >>> wbdata=requests.get(url).text
    >>> soup=BeautifulSoup(wbdata,'lxml')
    >>> news_titles=soup.select('div.text>em.f14>a.linkto')
    >>>
    >>> for n in news_titles:
    ... title=n.get_text()
    ... link=n.get('href')
    ... data={
    ... '标题':title,
    ... '链接':link
    ... }
    ... print(data)
    ...

    1. zmister说道:

      css选择器的>之间要有空格,不是div.text>em.f14>a.linkto而是div.text > em.f14 > a.linkto

      1. 未详吹牛中说道:

        感谢!

  9. aa说道:

    老师 像您站上的 a 是没有class 怎么选呢

    1. zmister说道:

      那就用其他的定位方式,比如xpath、与正则结合等等

  10. 匿名说道:

    为什么会出现 AttributeError: module 'requests' has no attribute 'get'
    出不来结果 :cry: :cry: :cry:

  11. ruanruan说道:

    出现报错,运行不下去怎么办 :cry: :cry: :cry:
    AttributeError: module 'requests' has no attribute 'get'

    1. zmister说道:

      模块有没有被赋值

  12. FIGO说道:

    写的真好,感谢

  13. 正一说道:

    我修改了被爬的对象,然后标题就乱码了。请帮我判断一下是怎么回事^^
    url改成:url = 'http://www.cyzone.cn/'
    定位改成:news_titles = soup.select('div.item-intro > a.item-title')

    现象:'标题': 'ã\x80\x8aæ\x88\x91ç\x9a\x84ä¸\x96ç\x95\x8cã\x80\x8bä¸\xadå\x9b½ç\x89\x88å¼\x80大ä¼\x9aï¼\x9a请尽æ\x83\x85ç\x8e©è\x80\x8då\x88\x9bé\x80\xa0ï¼\x8cç\x84¶å\x90\x8eå\x88\x86é\x92±', '链接': 'http://www.cyzone.cn/a/20180202/323770.html'

    1. zmister说道:

      网页编码问题

  14. FIGO说道:

    Traceback (most recent call last):
    File "D:\PythonSpace\gg.py", line 7, in
    soup_data=BeautifulSoup(data,'lxml')
    File "D:\Python33\lib\site-packages\bs4\__init__.py", line 192, in __init__
    elif len(markup) <= 256 and (
    TypeError: object of type 'Response' has no len()

    跟原文代码一样,只是url不同,返回了这样报错,请问大神是什么原因

    1. zmister说道:

      URL不同,可能页面结构也就不一样了。理解各个步骤的含义,requests.get相当于打开一个网页,BeautifulSoup则相当于从呈现的网页中复制一些文本。

  15. 匿名说道:

    老师,你是一个什么样的好人!写这么好的东西……
    顺便请教一下,for循环中的n.get中的n是什么

  16. 匿名说道:

    老师,你是一个什么样的好人!写这么好的东西……
    顺便请教一下,for循环中的n.get中的n是什么
    NameError: name 'n' is not found!

发表评论

电子邮件地址不会被公开。