写点什么

01- 大规模异步新闻爬虫:简单的新闻爬虫

作者:AI悦创
  • 2022 年 1 月 02 日
  • 本文字数:2939 字

    阅读完需:约 10 分钟

你好,我是悦创。


因为 CSDN 的限制:



完整版本点击:https://www.aiyc.top/2132.html


这个实战例子是构建一个大规模的异步新闻爬虫,但要分几步走,从简单到复杂,循序渐进的来构建这个 Python 爬虫。


要抓取新闻,首先得有新闻源,也就是抓取的目标网站。国内的新闻网站,从中央到地方,从综合到垂直行业,大大小小有几千家新闻网站。百度新闻(news.baidu.com)收录的大约两千多家。那么我们先从百度新闻入手。


打开百度新闻的网站首页:news.baidu.com我们可以看到这就是一个新闻聚合网页,里面列举了很多新闻的标题及其原始链接。如图所示:


我们的目标就是从这里提取那些新闻的链接并下载。流程比较简单:



根据这个简单流程,我们先实现下面的简单代码:


#!/usr/bin/env python3# Author: veelion

import reimport timeimport requestsimport tldextractheaders = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36",}

def save_to_db(url, html): # 保存网页到数据库,我们暂时用打印相关信息代替 print('%s : %s' % (url, len(html)))

def crawl(): # 1. download baidu news hub_url = 'https://news.baidu.com/' res = requests.get(hub_url, headers=headers) html = res.text
# 2. extract news links ## 2.1 extract all links with 'href' links = re.findall(r'href=[\'"]?(.*?)[\'"\s]' , html) print('find links:', len(links)) news_links = [] ## 2.2 filter non-news link for link in links: if not link.startswith('http'): continue tld = tldextract.extract(link) if tld.domain == 'baidu': continue news_links.append(link)
print('find news links:', len(news_links)) # 3. download news and save to database for link in news_links: # time.sleep(3) html = requests.get(link, headers=headers).text save_to_db(link, html) print('works done!')

def main(): while 1: crawl() time.sleep(300)

if __name__ == '__main__': main()
复制代码


简单解释一下上面的代码:


  1. 使用 requests 下载百度新闻首页;

  2. 先用正则表达式提取 a 标签的 href 属性,也就是网页中的链接;然后找出新闻的链接,方法是:假定非百度的外链都是新闻链接;

  3. 逐个下载找到的所有新闻链接并保存到数据库;保存到数据库的函数暂时用打印相关信息代替。

  4. 每隔 300 秒重复 1-3 步,以抓取更新的新闻。


以上代码能工作,但也仅仅是能工作,槽点多得也不是一点半点,那就让我们一起边吐槽边完善这个爬虫吧。

1. 增加异常处理

在写爬虫,尤其是网络请求相关的代码,一定要有异常处理。目标服务器是否正常,当时的网络连接是否顺畅(超时)等状况都是爬虫无法控制的,所以在处理网络请求时必须要处理异常。


网络请求最好设置 timeout,别在某个请求耗费太多时间。timeout 导致的识别,有可能是服务器响应不过来,也可能是暂时的网络出问题。所以,对于 timeout 的异常,我们需要过段时间再尝试。

2. 要对服务器返回的状态,如 404,500 等做出处理

服务器返回的状态很重要,这决定着我们爬虫下一步该怎么做。需要处理的常见状态有:


  • 301, 该 URL 被永久转移到其它 URL,以后请求的话就请求被转移的 URL

  • 404,基本上是这个网站已经失效了,后面也就别试了

  • 500,服务器内部出错了,可能是暂时的,后面要再次请求试试

3. 管理好 URL 的状态

记录下此次失败的 URL,以便后面再试一次。对于 timeout 的 URL,需要后面再次抓取,所以需要记录所有 URL 的各种状态,包括:


  • 已经下载成功

  • 下载多次失败无需再下载

  • 正在下载

  • 下载失败要再次尝试


增加了对网络请求的各种处理,这个爬虫就健壮多了,不会动不动就异常退出,给后面运维带来很多的工作量。下一节我们讲对上面三个槽点结合代码一一完善。欲知详情,请听下回分解。



Python 爬虫知识点

本节中我们用到了 Python 的几个模块,他们在爬虫中的作用如下:


  1. requests 模块


它用来做 http 网络请求,下载 URL 内容,相比 Python 自带的 urllib.request,requests 更加易用。GET,POST 信手拈来:


import requests
res = requests.get(url, timeout=5, headers=my_headers)
res2 = requests.post(url, data=post_data, timeout=5, headers=my_headers)
复制代码


get()post() 函数有很多参数可选,上面用到了设置 timeout,自定义 headers,更多参数可参考 requests 文档。​


requests 无论 get() 还是 post() 都会返回一个 Response 对象,下载到的内容就通过这个对象获取:


  • res.content 是得到的二进制内容,其类型是 bytes;

  • res.text 是二进制内容 content decode 后的 str 内容;


它先从 response headers 里面找到 encoding,没找到就通过 chardet 自动判断得到 encoding,并赋值给 res.encoding,最后把二进制的 content 解密为 str 类型。​


悦创经验: res.text 判断中文编码时有时候会出错,还是自己通过 cchardet(用 C 语言实现的 chardet)获取更准确。这里,我们列举一个例子:


In [1]: import requests
In [2]: r = requests.get('http://epaper.sxrb.com/index.shtml')
In [3]: r.encodingOut[3]: 'ISO-8859-1'
In [4]: import chardet
In [5]: chardet.detect(r.content)Out[5]: {'confidence': 0.99, 'encoding': 'utf-8', 'language': ''}
复制代码


上面是用 IPython 交互式解释器(强烈推荐 IPython ,比 Python 自己的解释器好太多)演示了一下。打开的网址是山西日报数字报,手动查看网页源码其编码是 utf8,用 chardet 判断得到的也是 utf8。


而 requests 自己判断的 encoding 是 ISO-8859-1,那么它返回的 text 的中文也就会是乱码。requests 还有个好用的就是 Session,它部分类似浏览器,保存了 cookies,在后面需要登录和与 cookies 相关的爬虫都可以用它的 session 来实现。


  1. re 模块


正则表达式主要是用来提取 html 中的相关内容,比如本例中的链接提取。更复杂的 html 内容提取,推荐使用 lxml 来实现。


  1. tldextract 模块这是个第三方模块,需要 pip install tldextract 进行安装。它的意思就是 Top Level Domain extract,即顶级域名提取。前面我们讲过 URL 的结构,news.baidu.com 里面的 news.baidu.com 叫做 host,它是注册域名 baidu.com 的子域名,而 com 就是顶级域名 TLD。它的结果是这样的:


In [2]: import tldextract
In [3]: tldextract.extract('http://news.baidu.com/')Out[3]: ExtractResult(subdomain='news', domain='baidu', suffix='com')
复制代码


返回结构包含三部分:subdomain, domain, suffix


  1. time 模块


时间,是我们在程序中经常用到的概念,比如,在循环中停顿一段时间,获取当前的时间戳等。而 time 模块就是提供时间相关功能的模块。同时还有另外一个模块 datetime 也是时间相关的,可以根据情况适当选择来用。


记住这几个模块,在今后的写爬虫生涯中将会受益匪浅。


AI 悦创·推出辅导班啦,包括「Python 语言辅导班、C++辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。QQ、微信在线,随时响应!V:Jiabcdefh



发布于: 3 小时前
用户头像

AI悦创

关注

浅者见浅,深者见深 2019.05.09 加入

个人简介:跟AI悦创实现你的一切脑洞! 公众号:AI悦创,百度文库认证作者,51CTO 签约讲师,原网易有道、清华团队答疑老师。博客:www.aiyc.top

评论

发布
暂无评论
01-大规模异步新闻爬虫:简单的新闻爬虫