Python 网络爬虫实战:爬取南方周末新闻文章(带关键词筛选)

Posted 机灵鹤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 网络爬虫实战:爬取南方周末新闻文章(带关键词筛选)相关的知识,希望对你有一定的参考价值。

1. 分析网站

南方周末,网站地址为:http://www.infzm.com/contents?term_id=1

Python

观察网站主页,我们可以了解到,网站左侧为 ​​频道列表​​ ,中间为 ​​新闻列表​​ 。

鼠标点击切换左侧的频道时,观察到浏览器地址栏中 ​​term_id​​ 的值同步发生变化,说明 ​​term_id​​ 参数表示频道的 ​​id​​ 。

将网页滚动条往下滑,观察到会不断有新的新闻文章加载进来,但是浏览器地址栏中的网址全程没有变化,说明新闻列表采用 ​​瀑布流​​ 的加载形式,数据通过 ​​Ajax​​ 动态加载。

简单分析之后,我们打开 ​​开发者工具​​ ,切换到 ​​Network​​ 页签开始抓包分析。

1.1 新闻列表分析

Python

在页面下滑的过程中,不断有新的请求出现。

请求的 URL 形如: http://www.infzm.com/contents?term_id=1&page=2&format=json

请求的内容如图所示:

Python

到这里我们知道了,这个便是我们要找的 ​​新闻列表​​ 的数据接口。

观察接口 URL:http://www.infzm.com/contents?term_id=1&page=2&format=json

有 3 个参数:​​term_id​​ ,​​page​​ 和 ​​format​​ 。

​term_id​​ 前面分析过了表示频道的 id,其他两个根据字面含义,​​page​​ 表示页数,​​format​​ 表示数据格式。

返回的数据格式是标准的 ​​json​​ ,文章列表数据位于 ​​data -> contents​​ ,包括文章标题,文章id,作者名字,发布时间等信息。

1.2 新闻详情页分析

随便打开一篇新闻文章的详情页,如:http://www.infzm.com/contents/217973 。

我们观察到详情页链接的构成方式为 ​​http://www.infzm.com/contents/​​ + ​​文章id​​ 。

通过开发者工具查看,了解到新闻正文内容渲染在 ​html​ 源码中。

Python

如图所示,新闻内容在 ​​<div class="nfzm-content__content">​​ 标签中。其中 ​​引言​​ 部分位于 ​​<blockquote class="nfzm-bq">​​ 标签下;正文内容位于 ​​<div class="nfzm-content__fulltext">​​ 标签下的 ​​p​​ 标签中。

网页结构示意如下:

<div class="nfzm-content__content">
<blockquote class="nfzm-bq">引言</blockquote>
<div class="nfzm-content__fulltext">
<p>第一段</p>
<p>第二段</p>
<p>第三段</p>
</div>
</div>

1.3 反爬机制分析

我们用 Python 简单编写一段代码,测试一下网站的反爬机制。

1.3.1 新闻列表

简单伪造一下 headers ,发起网络请求。

import requests

headers = {
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,
user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36,
}
url = "http://www.infzm.com/contents?term_id=1&page=2&format=json"
r = requests.get(url, headers=headers)
r.encoding = r.apparent_encoding
print(r.text)

发现可以正常获取到数据。

Python

1.3.2 新闻正文

import requests

headers = {
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,
user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36,
}
url = "http://www.infzm.com/contents/217973"
r = requests.get(url, headers=headers)
r.encoding = r.apparent_encoding
print(r.text)

新闻正文内容也可以成功获取到。

Python

不过并不是所有新闻正文都可以无障碍爬取到,有些新闻正文仅展示部分内容,全文需要登录账号之后才能查看。

Python

而当我注册好账号之后刷新界面,发现查看全文居然还要订阅会员。

Python

这里我就先不开通会员了。

如果有需要的同学,可以自行开通会员后,将登录后的 ​​cookies​​ 填入代码中的 ​​headers​​ 中,进行爬取。

headers = {
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,
user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36,
Cookie: "你自己的cookie"
}

Cookie 可以在开发者工具中查看。

2. 编码环节

接下来,开始正式编码。

首先导入这个爬虫程序需要用到的库

import requests
import json
from bs4 import BeautifulSoup
import os

然后是网络请求函数 ​​fetchUrl​

def fetchUrl(url):

功能:访问 url 的网页,获取网页内容并返回
参数:目标网页的 url
返回:目标网页的 html 内容

headers = {
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,
user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36,
}
try:
r = requests.get(url, headers=headers)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except Exception as e:
print(e)

解析新闻列表函数 ​​parseNewsList​

def parseNewsList(html):

功能:解析新闻列表页,提取新闻列表数据并依次返回
参数:列表数据(json 格式)
返回:新闻的id,标题,发布时间

try:
jsObj = json.loads(html)
contents = jsObj["data"]["contents"]
for cnt in contents:
pid = cnt["id"]
subject = cnt["subject"]
publish_time = cnt["publish_time"]
yield pid, subject, publish_time

except Exception as e:
print("parseNewsList error!")
print(e)

解析新闻正文内容函数 ​​parseNewsContent​

def parseNewsContent(html):

功能:解析新闻详情页,提取新闻正文内容并返回
参数:网页源码(html 格式)
返回:新闻正文内容的字符串

try:
bsObj = BeautifulSoup(html, "html.parser")
cntDiv = bsObj.find("div", attrs={"class": "nfzm-content__content"})
blockQuote = cntDiv.find("blockquote", attrs={"class": "nfzm-bq"})
fulltextDiv = cntDiv.find("div", attrs={"class": "nfzm-content__fulltext"})
pList = fulltextDiv.find_all("p")

ret = blockQuote.text + "\\n" if blockQuote else ""
ret += "\\n".join([p.text for p in pList if len(p.text) > 1])
return ret

except Exception as e:
print("parseNewsContent error!")
print(e)

保存文件函数 ​​saveFile​

def saveFile(path, filename, content):

功能:将文章内容 content 保存到本地文件中
参数:要保存的内容,路径,文件名

# 如果没有该文件夹,则自动生成
if not os.path.exists(path):
os.makedirs(path)
# 保存文件
with open(path + filename, w, encoding=utf-8) as f:
f.write(content)

爬虫调度器 ​​download_nfzm​

def download_nfzm(termId, page, savePath):

功能:爬取 termId 频道,第 page 页的所有新闻,并保存至 savePath 路径下
参数:termId 频道 Id
page 页码
savePath 保存路径

url = f"http://www.infzm.com/contents?term_id={termId}&page={page}&format=json"
html = fetchUrl(url)
try:
for Python爬虫实战教程:爬取网易新闻;爬虫精选 高手技巧

爬取腾讯网的热点新闻文章 并进行词频统计(Python爬虫+词频统计)

Python爬虫实战:爬取Drupal论坛帖子列表

爬虫实践爬取官方新闻标题正文时间

python3 怎么爬取新闻网站

网络爬虫百度新闻标题及链接爬取