作者:李禹锋,重庆芝诺大数据分析有限公司数据挖掘工程师。
昨天写了一个网站的整站爬虫,电脑一直处于高负荷运行的状态,没来得及更新文章。今天就以前几篇的基础知识,来进行爬虫练手。纯粹的看是学不会的,多实际操作,运行运行别人的代码,再修改别人的代码很快就能上手,日本著名数学家小平邦彦也曾说过模仿加改良等于创新嘛。
01
百度新闻爬虫小DEMO
第一个DEMO拿百度新闻为例,需求是以python爬虫为关键词,爬取百度新闻搜索的所有链接、标题和简介的内容。
首先第一步要思考一个问题,百度新闻是如何进行翻页操作的?在互联网上,翻页几乎算得上是最常见的操作之一了,当然方法也多种多样,甚至不少网站就以翻页的控制来进行反爬虫的控制。
不过我们以百度作为正式爬虫的第一个DEMO其实就是因为百度搜索没啥反爬虫。翻页的操作也是最简单的一种——通过调整发送的参数来进行页码的调整。下面来介绍一下什么是URL,如何来写参数。
例如在百度新闻中搜索了“python爬虫之后”,url变为了如下。
看似是一堆稀奇古怪的东西,但是一个词一个词挨着来看其实很简单。
开头的http://代表的是协议名,之前也曾说过协议有很多,目前只需了解http和https两种即可。
接下来的news.baidu.com代表的域名,可以理解为一个域名对应着一个公网的IP地址(需要找到这个对应表就需要DNS进行解析,不过这一部分只需了解即可)。
之后是一根斜线和ns,代表的是对方网站的路由,暂时可以简单的想象成是对方网站所在服务器的文件路径,就像是我们在windows的文件路径一样,在A文件夹之下的B文件夹下的C文件可以写成 A/B/C 。
再然后就是关键了,一个问号开头,后面以&符号进行拼接,每一个小块都是以 变量名=变量值 的形式呈现,这里的每一个小块都是一个参数。当然也并不是需要清楚所有的参数有什么用,想要了解的童鞋可以自行尝试。我们只需要关注最核心的一些参数(如何进行翻页的参数)。
上面那种形式是点击了百度一下之后的URL,虽然也有规律性,但这里给大家一个小技巧,在针对这种通过参数来进行翻页的网站进行爬虫时,先翻到后面的页数再倒回第一页,这样URL会变得更加规律一些。
搜索第一页的URL变为了如下
第二页URL
第三页URL
第四页URL。。。
发现规律了吗?其他的部分都没有变化,变化的只有pn一个参数,每一页有20条新闻,所以每翻一页url中的pn参数就加20(可以尝试改改rn参数,看看是什么效果,改成50试试)。
而URL在python中是字符串,那么通过字符串的拼接即可做到循环每一页的内容(学会找URL规律的方法,还是有不少网站是通过URL的参数进行翻页控制的)
那么下面就结合前两篇的内容,直接上代码了。
from urllib.request import Request,urlopen
from scrapy import Selector
for i in range(7): #直接看的有7页内容,偷个懒
url = "http://news.baidu.com/ns?word=python%E7%88%AC%E8%99%AB&pn="+str(i*20)+"&cl=2&ct=1&tn=news&rn=20&ie=utf-8&bt=0&et=0"
req = Request(url=url,headers={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'})
html = urlopen(req).read().decode('utf-8')
nodes = Selector(text=html).xpath('//div[@id="content_left"]/div[3]/div')
for j in range(len(nodes)):
res = {
'title':nodes[j].xpath('h3/a/text()').extract(),
'link_url':nodes[j].xpath('h3/a/@href').extract(),
'content':'div/div[2]/text()'
}
print(res)
02
CSDN小DEMO
如上小DEMO就实现了一个循环翻页爬虫的功能,下面的小DEMO是一个两层爬虫,即第一次爬虫获取到相关的链接,再循环爬取第一次获取到的链接。以CSDN搜索Python爬虫为例,搜索所有CSDN中关于Python爬虫的文章(还有视频教程以及下载资源等,这部分需要排除)。
目前还是以偷懒的方式来写,搜索后CSDN显示共有179560条结果,每页显示20条,即共有8978页(先这样理解吧,其实其中有不少是重复的,假设全部为不重复的。而且最关键问题在于,在这么多条结果的时候绝大部分网站的后台逻辑并不支持,因为没有几个真正的用户会看那么多页的内容,CSDN就是这样,普通的操作只提供前20页的内容,即400条数据)
代码如下(自行运行时需要运行一些时间,8978页+179560页,该爬虫需要发送188538个请求,也需要解析这么多个页面,目前还未涉及多线程、多进程爬虫,也还未涉及分布式集群爬虫,电脑性能不太好的童鞋慎用)
from urllib.request import Request,urlopen
from scrapy import Selector
from re import findall
for i in range(1,8979):
url='http://so.csdn.net/so/search/s.do?p='+str(i)+'&q=python%E7%88%AC%E8%99%AB&t=&o=&s=&u=&l=&f='
req = Request(url=url,headers={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'})
html = urlopen(req).read().decode('utf-8')
#获取到所有链接
links = Selector(text=html).xpath('//dl[@class="search-list" or @class="search-list J_search"]/dt/a/@href').extract()
#循环当前搜索页的所有链接
for link in links:
#运用正则表达式进行判断,该链接是否为文章(文章的URL中有/article/details/)
if findall(r'/article/details/',link):
req_link = Request(url=link,headers={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'})
html_link = urlopen(req_link).read().decode('utf-8')
#因CSDN文章含有HTML的格式,故可直接获取整篇文章的HTML,之后再行清洗整合在自己网站的页面中
article = Selector(text=html_link).xpath('//article').extract()
print(article)
03
初步优化CSDN小DEMO
以上两个案例的代码都是按照着纯流程化来书写的,可以发现其中有着不少重复代码,特别是CSDN的DEMO,例如发送请求时,变动的仅仅是URL,进行解析时,变动的仅仅是XPATH。故我们可以使用函数封装的方式来进行初步的代码优化。详细如下。
from urllib.request import Request,urlopen
from scrapy import Selector
from re import findall
def parse(url,xpath):
req = Request(url=url,headers={'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'})
html = urlopen(req).read().decode('utf-8')
return Selector(text=html).xpath(xpath).extract()
def main():
for i in range(1,8979):
url = 'http://so.csdn.net/so/search/s.do?p='+str(i)+'&q=python%E7%88%AC%E8%99%AB&t=&o=&s=&u=&l=&f='
links = parse(url=url,xpath='//dl[@class="search-list" or @class="search-list J_search"]/dt/a/@href')
for link in links:
if findall(r'/article/details/',link):
article = parse(url=link,xpath='//article')
print(article)
main()
放假通知
地球不爆炸,我们不放假
宇宙不重启,我们不休息
人生。。
领取专属 10元无门槛券
私享最新 技术干货