上一章咱们使用Python实现了一个简单的HTTP请求,瞧着简单,爬虫就是模拟人打开一个个URL浏览一个个网页来爬取数据的,一个成功的HTTP请求,就是一个爬虫的基础。
接下来,咱们以一个实际的例子:爬取百度新闻,来介绍使用BeautifulSoup对HTML进行解析处理。
爬取腾讯新闻
1、寻找数据特征
腾讯新闻的网址URL为:http://news.qq.com/
我们打开网页看看:
我们需要爬取这个页面每一条新闻的标题,鼠标右击一条新闻的标题,选择“审查元素”,出现下图的窗口:
图片中红框的位置就是那一条新闻标题在HTML中的结构、位置和表现形式:
<a target="_blank" class="linkto" href="http://news.qq.com/a/20170104/001280.htm">北京2016年39天重污染 PM2.5仍超国标一倍</a>
它上一级元素为:<em class=”f14 l24″>,再上一级元素为:<div class=”text”>
我们再看另一条新闻的标题,发现它的结构和之前我们分析的新闻标题的结构是一样的:
<div class="text"> <em class="f14 l24"> <a target="_blank" class="linkto" href="http://news.qq.com/a/20170104/000968.htm">台拟将蔡英文贺岁春联“自自冉冉”一词列入辞典 </a> </em> </div>
通过这些信息,我们就可以确定新闻标题在HTML文档中的位置。
接下来,我们开始使用Python对腾讯新闻标题进行爬取
** 2、编写爬取代码
首先上完整的代码
# coding:utf-8 # 引入相关模块 import requests from bs4 import BeautifulSoup url = "http://news.qq.com/" # 请求腾讯新闻的URL,获取其text文本 wbdata = requests.get(url).text # 对获取到的文本进行解析 soup = BeautifulSoup(wbdata,'lxml') # 从解析文件中通过select选择器定位指定的元素,返回一个列表 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)
运行程序,获取到的部分结果为如下所示:
C:\Python34\python.exe H:/python/scrap/scrap_tech_book/scrap_tennetNews.py {'标题': '2017年首个断崖降级官员曝光 他犯了什么事?', '链接': 'http://news.qq.com/a/20170104/002209.htm'} {'标题': '湄公河武装执法纪实:队员吃饭都要死死盯着前方', '链接': 'http://news.qq.com/a/20170104/007148.htm'} {'标题': '河南台前县出台彩礼“指导标准”:总数不得高于6万', '链接': 'http://news.qq.com/a/20170104/001693.htm'} {'标题': '摆射击摊获刑老太谈上诉原因:怕预订的咸菜浪费了', '链接': 'http://news.qq.com/a/20170104/013261.htm'} {'标题': '美国联军承认在叙伊有“误伤”:打死近二百平民', '链接': 'http://news.qq.com/a/20170104/005283.htm'} {'标题': '辽宁黑山企业人员退休先缴四百元:未经省级审批', '链接': 'http://news.qq.com/a/20170104/006323.htm'} {'标题': '罗布泊遇难者李中华的骨灰回家:身份仍有3大谜团', '链接': 'http://news.qq.com/a/20170104/003139.htm'} {'标题': '嫌楼上邻居吵,这家人买“震楼神器”反击', '链接': 'http://news.qq.com/a/20170104/001582.htm'} {'标题': '12岁女孩疑似被弃 因妈妈买不起她的火车票?', '链接': 'http://news.qq.com/a/20170104/004622.htm'} {'标题': '向美国商务部说“NO” 这家中国民企打赢美国官司', '链接': 'http://news.qq.com/a/20170104/000322.htm'} {'标题': '台拟将蔡英文贺岁春联“自自冉冉”一词列入辞典', '链接': 'http://news.qq.com/a/20170104/000968.htm'} {'标题': '新华社:姚明资历经验尚浅 任篮协主席尚有障碍', '链接': 'http://news.qq.com/a/20170104/000330.htm'} {'标题': '新京报:法院拍卖BB弹玩具枪,自己人也糊涂?', '链接': 'http://news.qq.com/a/20170104/001344.htm'}
这正是我们所需要的。
虽然代码很简单,但还是做一点点讲解,方便刚刚接触的同学。
3、代码解析
# coding:utf-8
首先,我们定义了文件的编码形式为UTF-8,以避免一些编码错误导致中文乱码。
import requests from bs4 import BeautifulSoup
然后,我们引入了相关的模块,requests用于HTTP请求,BeautifulSoup用于解析HTML响应。
url = "http://news.qq.com/"
设置一个变量url,值为腾讯新闻的URL
wbdata = requests.get(url).text
使用requests.get()对URL发起GET方式的HTTP请求,并使用text()方法获取响应的文本内容,最后将其赋值给变量wbdata。
soup = BeautifulSoup(wbdata,'lxml')
使用BeautifulSoup对响应文本wbdata进行解析处理,这里使用的是lxml库,如何没有安装,可以使用Python自带的html.parser,效果也是一样的。
news_titles = soup.select("div.text > em.f14 > a.linkto")
在解析后的文本中,使用select选择器,在文本中选择指定的元素,通常我们还会使用find()和findall()方法来进行元素选择。
这一步返回的为一个列表,列表内的元素为匹配的元素的HTML源码。
for n in news_titles: # 提取出标题和链接信息 title = n.get_text() link = n.get("href") data = {'标题':title,'链接':link} print(data)
对结果列表进行遍历,再从遍历的元素中提取出数据,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
文章版权所有:州的先生博客,转载必须保留出处及原文链接
博主你第一段里写成百度新闻了
失误失误,开始是选百度新闻的,后来换成爬腾讯
代码第十三行的选择器中的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也会报错,这是为什么,我几乎完全小白,希望博主解答
没错了,照博主代码写可以出结果,但就是还是不懂那个选择器里的div.text > em.f14 > a.linkto是怎么出来的
div.text > em.f14 > a.linkto表示
额,这个你需要了解一下CSS选择器的基本的概念,从调试控制台copy出来的是其对于整个页面的CSS选择器位置,我设置的那个是提取简化之后的,要看具体页面来,>符号表示html元素的下一层级关系
我是python3.5:"div.text > em.f14 > a.linkto"这一块,“>”两边需要有空格.
W3C文档中讲的是“”CSS选择器子结合符两边可以有空白符,这是可选的”
赞赞!
为什么我爬到的都是{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'} 的
这个你用utf-8编码解码一下就好了
怎么解码,我的也是 {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'}
参考这篇加百度正则爬了一个网站的动漫图,谢谢指导
为什么我跑出来会报错,提示:
bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?
博主,“工具准备”篇中把安装lxml库加上吧,实测得先pip install lxml,不然按照本文方法写的代码跑不了
哈哈,失误失误,多谢指正
请问各位大师,这个程序为什么没有反应?还望赐教指点,小弟拜谢
>>> # 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)
...
css选择器的>之间要有空格,不是div.text>em.f14>a.linkto而是div.text > em.f14 > a.linkto
感谢!
老师 像您站上的 a 是没有class 怎么选呢
那就用其他的定位方式,比如xpath、与正则结合等等
为什么会出现 AttributeError: module 'requests' has no attribute 'get'
出不来结果
出现报错,运行不下去怎么办
AttributeError: module 'requests' has no attribute 'get'
模块有没有被赋值
写的真好,感谢
我修改了被爬的对象,然后标题就乱码了。请帮我判断一下是怎么回事^^
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'
网页编码问题
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不同,返回了这样报错,请问大神是什么原因
URL不同,可能页面结构也就不一样了。理解各个步骤的含义,requests.get相当于打开一个网页,BeautifulSoup则相当于从呈现的网页中复制一些文本。
那这种问题如何解决啊,不同的url可能出现这个错误
老师,你是一个什么样的好人!写这么好的东西……
顺便请教一下,for循环中的n.get中的n是什么
老师,你是一个什么样的好人!写这么好的东西……
顺便请教一下,for循环中的n.get中的n是什么
NameError: name 'n' is not found!
老师,我今天照着你的代码爬取腾讯新闻,但是得到的结果只有一个标题和链接({'标题': '黄毅清自称年轻不懂事,为女儿愿向黄奕低头求复合', '链接': 'http://new.qq.com/omn/20181213A1CS3N.html'})。输出new_titles得到的是[“俄罗斯女特工”在美认罪:影响特朗普对俄政策, 法国检察部门:警方已将斯特拉斯堡枪案嫌犯击毙,......],有人和我说转化为json可以,但我不会,请问一下这样怎么解决???谢谢
不需要转json,你对new_titles使用for循环进行遍历即可。
现在应该爬不了了吧,我换个网站可以爬。
嗯,知道原理就可以
请问你现在换哪个网站爬的呀
hello
怎么下载print(data)到本地txt file中
我是这么写的
fo=open(ubuntu/Desktop/news.txt,'w')
fo.write(str(data))
fo.close
得到的结果只有第一条