当前位置: 首页 > 科技观察

如何用100行Python代码实现一个新闻爬虫?这算不算成功?

时间:2023-03-14 20:50:35 科技观察

每天上班都要坐地铁,地铁里信号差。但是我想在坐地铁的时候看一些新闻,所以我写了下面的新闻爬虫。我无意做一个漂亮的应用,所以只完成了原型,能满足我最基本的需求。思路很简单:找到新闻来源;用Python获取新闻;使用BeautifulSoup分析HTML并提取内容;将其转换为易于阅读的格式并通过电子邮件发送。下面详细介绍各部分的实现。新闻来源:RedditReddit是一个很棒的新闻来源,因为我们可以提交新闻链接并通过Reddit为它们投票。但接下来的问题是:如何才能获取每天最热门的新闻?在考虑爬取之前,首先要考虑目标网站是否提供了API。因为使用API是完全合法的,更重要的是它提供了机器可读的数据,所以不需要解析HTML。幸运的是,Reddit提供了一个API。我们可以从API列表中找到所需的功能:/top。该函数可以返回Reddit或指定subreddit上最热门的新闻。下一个问题是:如何使用这个API?通读了Reddit的文档后,我找到了最有效的用法。第1步:在Reddit上创建一个应用程序。登录后转到“首选项→应用程序”页面,底部有一个名为“创建另一个应用程序...”的按钮。单击以创建“脚本”类型的应用程序。我们不需要提供“关于url”或“重定向url”,因为此应用不对外开放,不会被其他人使用。创建应用后,在应用信息中可以找到AppID和Secret。接下来的问题是如何使用AppID和Secret。由于我们只需要获取给定SubReddit上最热门的新闻,而无需访问任何用户相关信息,理论上我们不需要提供用户名或密码等个人信息。Reddit提供了一种“仅限应用程序的OAuth”形式(https://github.com/reddit-archive/reddit/wiki/OAuth2#application-only-oauth),其中应用程序可以匿名访问公开可用的信息。运行以下命令:$curl-XPOST-H'User-Agent:myawesomeapp/1.0'-dgrant_type=client_credentials--user'OUR_CLIENT_ID:OUR_CLIENT_SECRET'https://www.reddit.com/api/v1/access_token这个命令访问将返回令牌:{"access_token":"ABCDEFabcdef0123456789","token_type":"bearer","expires_in":3600,"scope":"*"}太棒了!一旦你有了访问令牌,你就可以大展拳脚了。最后,如果不想自己写API接入代码,可以使用Python客户端:https://github.com/praw-dev/praw先做个测试,从/r获取最热门的5条新闻/Python:>>>importpraw>>>importpprint>>reddit=praw.Reddit(client_id='OUR_CLIENT_ID',...client_secret='OUR_SECRET',...grant_type='client_credentials',...user_agent='mytestscript/1.0')>>>subs=reddit.subreddit('Python').top(limit=5)>>>pprint.pprint([(s.score,s.title)forsinsubs])[(6555,'Automatetheboringstuffwithpython-tinder'),(4548,'MS正在考虑将Python与Excel进行官方集成,并要求''输入'),(4102,'PythonCheetSheetforbegineers'),(3285,'我们开始晚了,但我们设法将Python足迹留在了r/place上!'),(2899,"PythonSectionatFoyle]")'s,成功的!获取新闻页面下一个任务是获取新闻页面,这实际上非常简单。通过上一步,我们可以得到Submission对象,它的URL属性就是新闻的地址。我们也可以通过domain属性过滤掉那些属于Reddit本身的URL:res=requests.get(sub.url)if(res.status_code==200and'content-type'inres.headersandres.headers.get('content-type').startswith('text/html')):html=res.text这里我们跳过内容类型不是text/html的新闻地址,因为Reddit用户可能会提交图片的直接链接,我们不需要这个。提取新闻内容下一步是从HTML中提取内容。我们的目标是提取新闻的标题和正文,可以忽略其他不需要阅读的内容,比如页眉、页脚、侧边栏等。这个任务难度很大,目前还没有通用的完美解决方案。BeautifulSoup虽然可以帮我们提取文本内容,但是它会将页眉和页脚一起提取。幸运的是,我发现现在的网站结构比以前好多了。没有表格布局,也没有
,整个文章页面明明是用

来标记标题和每一段。而且大多数网站都会把标题和正文放在同一个容器元素中,比如这样:

SiteNavigation
PageTitle

Paragraph1

Paragraph2

版权所有...
本例中的顶级是标题和文本的容器。所以你可以使用下面的算法来查找文本:查找

作为标题。出于SEO目的,通常页面上只有一个

;找到

的父元素,检查父元素是否包含足够的

;重复第2步,直到找到一个包含足够多

的父元素,或到达元素。如果找到包含足够

的父元素,则该父元素就是文本的容器。如果在找到足够的

之前遇到,则该页面不包含任何可读内容。虽然这个算法很简单,没有考虑任何语义信息,但是是完全可行的。毕竟当算法失败时,你只需要忽略那篇文章,少看一篇也没什么大不了的……当然,你可以通过解析

,
等语义元素来实现更多或#main、.sidebar等精确算法。使用此算法可以轻松编写解析代码:soup=BeautifulSoup(text,'html.parser')#findthearticletitleh1=soup.body.find('h1')#findthecommonparentfor

andall

s.root=h1whileroot.name!='body'andlen(root.find_all('p'))<5:root=root.parentiflen(root.find_all('p'))<5:returnNone#findallthecontentelements.ps=root.find_all(['h2','h3','h4','h5','h6','p','pre'])这里我使用len(root.find_all('p'))<5作为文本过滤条件,因为真实新闻不太可能少于5段。您可以根据需要调整此值。转换为人类可读的格式最后一步是将提取的内容转换为人类可读的格式。我选择了Markdown,但你可以编写更好的转换器。在这个例子中,我只提取了

,所以一个简单的函数就可以满足要求:ps=root.find_all(['h2','h3','h4','h5','h6','p','pre'])ps.insert(0,h1)#addthetitlecontent=[tag2md(p)forpinps]deftag2md(tag):iftag.name=='p':returntag。texteliftag.name=='h1':returnf'{tag.text}\n{"="*len(tag.text)}'eliftag.name=='h2':returnf'{tag.text}\n{"-"*len(tag.text)}'eliftag.namein['h3','h4','h5','h6']:returnf'{"#"*int(tag.name[1:])}{tag.text}'eliftag.name=='pre':returnf'```\n{tag.text}\n```'完整代码我在Github上分享了完整代码,链接如下:https://gist.github.com/charlee/bc865ba8aac295dd997691310514e515恰好是100行,尝试运行:Scraping/r/Python...-Retrievinghttps://imgs.xkcd.com/comics/python_environment.pngxfailornothtml-Retrievinghttps://thenextweb.com/dd/2017/04/24/universities-finally-realize-java-bad-introductory-programming-language/#.tnw_PLAz3rbJ=>done,title="UniversitiesfinallyrealizethatJavaisabadintroductoryprogramminglanguage"-检索https://github.com/麻木/麻木y/blob/master/doc/neps/dropping-python2.7-proposal.rstxfailornothtml-Retrievinghttp://www.thedurkweb.com/sms-spoofing-with-python-for-good-and-evil/=>完成,title="SMSSpoofingwithPythonforGoodandEvil"...爬取新闻文件:最后要做的就是把这个脚本放到服务器上,设置cronjob每天运行一次,然后将生成的文件发送到我的邮箱。我没有花太多时间关注细节,所以其实这个脚本还有很大的改进空间。有兴趣的可以继续添加更多的功能,比如提取图片等等。