Python爬虫:抓取新浪新闻数据

栏目: Python · 发布时间: 7年前

内容简介:Python爬虫:抓取新浪新闻数据

案例一

抓取对象:

新浪国内新闻( http://news.sina.com.cn/china/ ),该列表中的标题名称、时间、链接。

Python爬虫:抓取新浪新闻数据

完整代码:

 1 from bs4 import BeautifulSoup
 2 import requests
 3 
 4 url = 'http://news.sina.com.cn/china/'
 5 web_data = requests.get(url)
 6 web_data.encoding = 'utf-8'
 7 soup = BeautifulSoup(web_data.text,'lxml')
 8 
 9 for news in soup.select('.news-item'):
10     if(len(news.select('h2')) > 0):
11         h2 = news.select('h2')[0].text
12         time = news.select('.time')[0].text
13         a = news.select('a')[0]['href']
14         print(h2,time,a)

运行结果: (只展示部分)

Python爬虫:抓取新浪新闻数据

详细解说:

1. 首先插入需要用到的库:BeautifulSoup、requests,然后解析网页。解析完后print下,确认是否解析正确。

1 from bs4 import BeautifulSoup
2 import requests
3 
4 url = 'http://news.sina.com.cn/china/'
5 web_data = requests.get(url)
6 soup = BeautifulSoup(web_data.text,'lxml')
7 print(soup)

这时,我们可以看到,解析出来的网页里面有很多乱码,并没有正确解析。观察下结果,看到开头的这句:

<meta content="text/html; charset=utf-8" http-equiv="Content-type"/>

【charset=utf-8】表示当前内容的字符集是采用utf-8编码格式,所以我们需要用encoding来解锁下,这时就能解析出来正常内容。

1 from bs4 import BeautifulSoup
2 import requests
3 
4 url = 'http://news.sina.com.cn/china/'
5 web_data = requests.get(url)
6 web_data.encoding = 'utf-8'
7 soup = BeautifulSoup(web_data.text,'lxml')
8 print(soup)

2. 解析出网页后,开始抓取我们需要的内容。首先,先补充几点知识。

看下面代码中的第一行,soup.select('.news-item'),取出含有特定CSS属性的元素时,比如:

  • 找出所有class为news-item的元素,class名前面需要加点(.),即英文状态下的句号;
  • 找出所有id为artibodyTitle的元素,id名前面需要加井号(#)。

另外,取得含有特定标签的HTML元素时,直接在select后写标签名即可,如下面for循环中的第3行,news.select('h2')。

1 for news in soup.select('.news-item'):
2     # print(news)
3     if(len(news.select('h2')) > 0):
4         # print(news.select('h2')[0].text)
5         h2 = news.select('h2')[0].text
6         time = news.select('.time')[0].text
7         a = news.select('a')[0]['href']
8         print(h2,time,a)

现在来详细看下这段代码每行的释义。

第1行:soup.select('.news-item'),取出news-item该类中的元素;

第2行:print下news,查看是否被正常解析,正常后继续,不用时可以注释掉;

第3行:通过观察代码,可以看到标题被储存在标签h2中,如果h2的长度大于0,这里是为了去除为空的标题数据;

第4行:print中news.select('h2')[0].text,[0]是取该列表中的第一个元素,text是取文本数据,print后查看是否正确,不用时可以注释掉;

第5行:将news.select('h2')[0].text存储在变量h2中;

第6行:time是class类型,前面加点来表示,同上,将其数据存储在变量time中;

第7行:我们要抓取的链接存放在a标签中,链接已经不是text了,后面用href,将链接数据存储在变量a中;

第8行:最后输出我们想要抓取的数据,标题、时间、链接。

案例二

抓取对象:

抓取新闻详情页的标题、时间(进行格式转换)、新闻来源、新闻详情、责任编辑、评论数量和新闻ID。

示例新闻: http://news.sina.com.cn/c/nd/2017-05-08/doc-ifyeycfp9368908.shtml

完整代码: 

 1 from bs4 import BeautifulSoup
 2 import requests
 3 from datetime import datetime
 4 import json
 5 import re
 6 
 7 news_url = 'http://news.sina.com.cn/c/nd/2017-05-08/doc-ifyeycfp9368908.shtml'
 8 web_data = requests.get(news_url)
 9 web_data.encoding = 'utf-8'
10 soup = BeautifulSoup(web_data.text,'lxml')
11 title = soup.select('#artibodyTitle')[0].text
12 print(title)
13 
14 time = soup.select('.time-source')[0].contents[0].strip()
15 dt = datetime.strptime(time,'%Y年%m月%d日%H:%M')
16 print(dt)
17 
18 source = soup.select('.time-source span span a')[0].text
19 print(source)
20 
21 print('\n'.join([p.text.strip() for p in soup.select('#artibody p')[:-1]]))
22 
23 editor = soup.select('.article-editor')[0].text.lstrip('责任编辑:')
24 print(editor)
25 
26 comments = requests.get('http://comment5.news.sina.com.cn/page/info?version=1&format=js&channel=gn&newsid=comos-fyeycfp9368908&group=&compress=0&ie=utf-8&oe=utf-8&page=1&page_size=20')
27 comments_total = json.loads(comments.text.strip('var data='))
28 print(comments_total['result']['count']['total'])
29 
30 news_id = re.search('doc-i(.+).shtml',news_url)
31 print(news_id.group(1))

运行结果:

国土部:5月到9月实行汛期地质灾害日报告制度

2017-05-08 17:21:00

央视新闻

原标题:国土资源部:地质灾害高发期 实行日报告制度

国土资源部消息,5月份将逐渐进入地质灾害的高发期,防灾减灾形势更加严峻。据中国气象局预计,5月份我国江南大部、华南东部、西北地区大部降水较常年同期偏多,应加强防范极端气象事件诱发的滑坡、泥石流等地质灾害。对此从5月起至9月,国土资源部应急办实行汛期地质灾害日报告制度,各地必须将每天发生的灾情险情及其重大工作部署于当天下午3点前报告国土资源部应急办。

李伟山

4

fyeycfp9368908

详细解说:

1. 首先插入需要用到的库:BeautifulSoup、requests、datetime(时间处理)、json(解码:把json格式字符串解码转换成 Python 对象)、re(正则表达式),然后解析网页。

先抓取标题:

 1 from bs4 import BeautifulSoup
 2 import requests
 3 from datetime import datetime
 4 import json
 5 import re
 6 
 7 url = 'http://news.sina.com.cn/c/nd/2017-05-08/doc-ifyeycfp9368908.shtml'
 8 web_data = requests.get(url)
 9 web_data.encoding = 'utf-8'
10 soup = BeautifulSoup(web_data.text,'lxml')
11 title = soup.select('#artibodyTitle')[0].text
12 print(title)

datetime、json、re在后面时间转换和从js中抓取数据时会用到。

我们主要来看下倒数第二行代码:title = soup.select('#artibodyTitle')[0].text,这里的用法和案例一中一样,id前用井号(#)来表示该类元素的位置,解锁唯一元素用[0],提取文本信息text。

2. 抓取时间,并将原有日期格式转化为标准格式:

1 # time = soup.select('.time-source')[0]
2 # print(time)
3 time = soup.select('.time-source')[0].contents[0].strip()
4 dt = datetime.strptime(time,'%Y年%m月%d日%H:%M')
5 print(dt)

第1行:抓取时间;

第2行:print下时间,这时会发现运行结果中既含有时间,还有新闻的来源,如下:

<span class="time-source" id="navtimeSource">2017年05月08日17:21  <span> <span data-sudaclick="media_name"><a href="http://m.news.cctv.com/2017/05/08/ARTIPEcvpHjWzuGDPWQhn77z170508.shtml" rel="nofollow" target="_blank">央视新闻</a></span></span> </span>

那么,接下来我们需要想办法将时间和来源分开来,这时需要使用到contents;

第3行:我们先在后面加上.contents,运行下后会看到上面的内容会在列表中分为如下2个元素,此时,我们取的时间为第一个元素,在contents后加[0],最后的.strip()可以去除时间末尾的\t;

['2017年05月08日17:21\t\t', <span>   <span data-sudaclick="media_name"><a href="http://m.news.cctv.com/2017/05/08/ARTIPEcvpHjWzuGDPWQhn77z170508.shtml" rel="nofollow" target="_blank">央视新闻</a></span></span>, '\n']

第4行:用datetime.strptime()来做时间格式化,原有的时间是年月日时分,所以转化时用到年月日时分,其中%Y是四位数的年份表示、%m是月份、%d是月份内的一天、%H是24小时制中的小时、%M是分钟数,并将其存储在变量dt中;

第5行:输出dt,会得到格式化后的时间,比如:2017-05-08 17:21:00。

3. 抓取新闻来源:

之前文章 《Python爬虫:爬取人人都是产品经理的数据》 中有提到可以用【Copy selector】来复制粘贴出新闻来源的位置,如下第一行;也可以用本篇文章经常用到的class类的表述方法说明其位置,如下第二行;

1 # source = soup.select('#navtimeSource > span > span > a')[0].text
2 source = soup.select('.time-source span span a')[0].text
3 print(source)

4. 抓取新闻详情:

1 article = []
2 for p in soup.select('#artibody p')[:-1]:
3     article.append(p.text.strip())
4 # print(article)
5 print('\n'.join(article))

第1行:article为空列表;

第2行:通过观察代码可以发现,新闻详情存放在p标签中,如果直接输出,能看到最后一栏有责任编辑的信息,如果不想要责任编辑,增加[:-1]可以去除最后一个编辑信息;

第3行:将抓取的数据插入article列表中,.strip()去除空白信息;

第4行:可以先print下看结果是否正确,发现新闻详情的原标题和正文是在一排的,不是很好,注释掉后我们换新的方法;

第5行:join()方法用来将列表中的元素用指定的字符连接生成新的字符串,这里用\n换行来连接列表中的原标题和正文两块内容。

上面这是一种文章连接的方法,当然还有比较简洁的写法。我们将上面的for循环一句拿出来,前面加p.text.strip(),然后整体加中括号,这样就形成了一个列表,然后前面再用join将其连接起来,就用简单的一行代码替代了上面的多行代码。

1 print('\n'.join([p.text.strip() for p in soup.select('#artibody p')[:-1]]))

5. 抓取责任编辑:

这里用到的lstrip是去除左侧的内容,括号内‘责任编辑:’是指去除这部分内容,只保留编辑人员姓名。之前用到的strip是去除两侧,lstrip是去除左侧,后面会用到的rstrip是去除右侧内容。

1 editor = soup.select('.article-editor')[0].text.lstrip('责任编辑:')
2 print(editor)

6. 抓取评论数:

1 # comments = soup.select('#commentCount1')
2 # print(comment)
3 comments = requests.get('http://comment5.news.sina.com.cn/page/info?version=1&format=js&channel=gn&newsid=comos-fyeycfp9368908&group=&compress=0&ie=utf-8&oe=utf-8&page=1&page_size=20')
4 # print(comments.text)
5 comments_total = json.loads(comments.text.strip('var data='))
6 # print(comments_total)
7 print(comments_total['result']['count']['total'])

首先,我们先select('#commentCount1')来筛选出评论数,这时print下输出结果为[<span id="commentCount1"></span>],span中间为空白信息,没有我们想要的评论数。

这时,我们需要重新去观察下网页代码,发现评论数可能是通过JavaScript来增加上去的,那么我们需要找到是从哪里调用JavaScript(即JS)的。

鼠标放在评论数(4)上,Google浏览器鼠标右键点击“检查”,选择顶部的network,然后在下面海量的链接里找到有数量4的链接。

Python爬虫:抓取新浪新闻数据

复制该链接,中间加粗部分是newid,最后有一段像时间戳的字串“&jsvar=loader_1494295822737_91802706”,我们需要把这块内容去掉,然后试着print下,可以发现结果也不会有影响。

http://comment5.news.sina.com.cn/page/info?version=1&format=js&channel=gn&newsid=comos-fyeycfp9368908&group=&compress=0&ie=utf-8&oe=utf-8&page=1&page_size=20&jsvar=loader_1494295822737_91802706

处理好链接后,那么,我们该如何从JavaScript中读取数据呢?这时我们需要使用到json来处理,在开头,我们导入库的时候已经import json了,这里可以直接使用。

json.loads是用于解码json数据的。第5行,我们将解码后的数据存储在变量comments_total中;第6行,我们print该变量时,会得到结果,发现除了评论数之外,还有一些其他的信息。

所以我们需要重新输出,根据上一行print的结果,观察发现,最后一行我们可以用['result']['count']['total']来表示评论数的位置。

7. 抓取新闻ID:

1 print(news_url.split('/')[-1].rstrip('.shtml').lstrip('doc-i'))

刚在上面第6点抓取评论数时,我们发现链接中有一个newsid,该新闻页面的链接中也有相同的ID部分,这时我们可以明确其新闻ID的位置。

split()是通过指定分隔符对字符串进行切片,[-1]是截取最后一个元素,然后用rstrip去除掉末尾的.shtml,用lstrip去除掉左侧的doc-i,得到我们想要的新闻ID。

除了这种方法外,我们还可以用正则表达式来表达。这时我们需要用到re库,在开头时,我们已经事先import re了,这里直接使用。

其中re.search是扫描整个字符串并返回第一个成功的匹配,group()是匹配到的字符串,其中group(0),是匹配到的字符串全部显示(doc-ifyeycfp9368908.shtml),group(1)是(.+)内的显示,即我们想要的新闻ID。

1 news_id = re.search('doc-i(.+).shtml',news_url)
2 # print(news_id.group(0))
3 print(news_id.group(1))

操作环境:Python版本,3.6;PyCharm版本,2016.2;电脑:Mac

-----   End   -----

作者:杜王丹,微信公众号:杜王丹,互联网产品经理。

Python爬虫:抓取新浪新闻数据


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Introduction to Computation and Programming Using Python

Introduction to Computation and Programming Using Python

John V. Guttag / The MIT Press / 2013-7 / USD 25.00

This book introduces students with little or no prior programming experience to the art of computational problem solving using Python and various Python libraries, including PyLab. It provides student......一起来看看 《Introduction to Computation and Programming Using Python》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试