这期的爬虫是爬取“简书”的搜索结果页,篇幅将会分为两部分来写,第一部分是爬虫部分,主要涉及搜索文章的提取和数据保存,第二部分涉及基本的数据分析和可视化,本篇文章属于爬虫篇。html
首先看一下整个爬虫的源代码,每一个函数的用处已经写在函数说明中,后面也会进行代码解读。python
# -*- coding: utf-8 -*- import requests import json from urllib.parse import quote from pymongo import MongoClient """ 简书搜索爬虫 输入搜索关键词,将搜索到的全部文章爬取下来 数据保存到 mongodb 中 """ class JianshuSearch(object): def __init__(self, db_name, coll_name, key, host='127.0.0.1', port=27017): self.headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/57.0.2987.110 Safari/537.36" } self.url = 'http://www.jianshu.com/search/do?q={key}&type=note&page={page}&order_by=default' self.key = quote(key) self.start_page = 1 self.host = host self.port = port self.db_name = db_name self.coll_name = coll_name def get_total_pages(self): '''提取总页码数''' url = self.url.format(key=self.key, page=self.start_page) html = requests.get(url, headers=self.headers).text data = json.loads(html) total_pages = data['total_pages'] return total_pages def get_infos(self, page): '''提取单个页面的文章信息,格式为dict''' url = self.url.format(key=self.key, page=page) html = requests.get(url, headers=self.headers).text data = json.loads(html) entries = data['entries'] for each in entries: self.save_infos(each) def save_infos(self, entry): '''保存一个文章的信息''' coon = MongoClient(host=self.host, port=self.port) coll = coon[self.db_name][self.coll_name] coll.insert(entry) def main(self): '''主函数,循环迭代进行翻页,提取全部页码的信息并保存到数据库''' total_pages = int(self.get_total_pages()) for i in range(1, total_pages + 1): self.get_infos(i) print('总计{}页,已经爬完{}页'.format(total_pages, i)) if __name__ == '__main__': DB_NAME = 'jianshu' COLL_NAME = 'search_result' key = 'python' spider = JianshuSearch(key=key, db_name=DB_NAME, coll_name=COLL_NAME) spider.main()
爬虫的基本思路依然分为3个主要步骤:ajax
首先在简书的搜索框中输入任意一个关键词,例如“Python”,而后点击搜索按钮,查看结果页面。mongodb
结果页面以下图所示:数据库
能够看到,搜索的结果包含了不少种类的信息,有“相关用户”、“相关专题”和“相关文章”,而且文章的排序还能够选择条件。json
因为这里只须要提取搜索文章的信息,所以能够先看一下搜索到的文章列表中包含哪些能够收集的信息,以便后续查找和保存数据。浏览器
固然,通常来讲,不少时候,咱们看到的信息其实并非眼前的页面给咱们的信息,而是当前页面从其余地方“搬”过来的信息。若是使用或者知道ajax 的用法,这句话就很好理解,固然,不理解也无妨,并不影响后续操做。ide
打开浏览器的开发者界面,可使用快捷键 F12。刷新一下搜索的页面,而后进入 Network,看看浏览器中加载了什么页面。函数
通常能够先从 Doc中查看,也就是网页源代码,可是此次的爬虫能够看到源代码中并无搜索的结果页,因而能够继续查看 js,发现好像也没有新加载页面,继续查看 XHR,终于找到了刷新页面加载出来的页面请求,具体看截图:工具
看截图中的编号,编号1就是页面加载的地方,编号2能够看到请求的 URL,编号3就是 URL 的组成,能够从中查看连接的构成规律,这里的规律就是连接中有2个关键参数,第一个参数就是搜索的关键词,这里用 q=Python表示,而后第二个参数就是当前页码,这里是 page=2,从这里就能够直接联想到,若是知道总页码数,那就可使用循环来获得全部页码的 URL。编号4是请求的方式,这里是 GET。
通过这个页面,就能够把整个爬虫的思路理清楚了:
首先须要导入相关库:
import requests import json from urllib.parse import quote from pymongo import MongoClient
这4个库的做用分别是:
因为这个爬虫是建立了一个爬虫类,因此须要按照 Python类的规范来,首先初始化类,经过函数来实现:
def __init__(self, db_name, coll_name, key, host='127.0.0.1', port=27017): self.headers = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/57.0.2987.110 Safari/537.36" } self.url = 'http://www.jianshu.com/search/do?q={key}&type=note&page={page}&order_by=default' self.key = quote(key) self.start_page = 1 self.host = host self.port = port self.db_name = db_name self.coll_name = coll_name
这里给类传递了5个参数,其中3个是没有给予默认值的参数,2个是给了默认值的参数,每一个参数的含义:
能够经过爬取任意一个页码,而后打印一下提取的信息,看看信息的构成。
经过打印信息能够发现,每一个页面的信息都是一个字典格式,所以能够经过json模块来转换成 Python 的 dict 格式,而后能够发现每页都有一个参数是“total_pages”,这个参数就提供了当前搜索的结果总页码数,所以能够经过函数来提取这个参数的值:
def get_total_pages(self): '''提取总页码数''' url = self.url.format(key=self.key, page=self.start_page) html = requests.get(url, headers=self.headers).text data = json.loads(html) total_pages = data['total_pages'] return total_pages
因为每一个页面的信息都是一个 json 格式,因此信息的提取方式很简单,直接从字典中提取就好了:
def get_infos(self, page): '''提取单个页面的文章信息,格式为dict''' url = self.url.format(key=self.key, page=page) html = requests.get(url, headers=self.headers).text data = json.loads(html) entries = data['entries'] for each in entries: self.save_infos(each)
函数的思路很简单,首先经过 requests
请求网页,而后获得一个json类型的信息,可是因为这个信息的格式是 str
, 因此须要使用 json.loads
方法转换成 dict
格式。而后使用字典的键值对关系提取到总页码数就能够了。
这个函数的最后面用到了一个保存信息的函数,也就是后面要说的将数据保存到数据库中。
def save_infos(self, entry): '''保存一个文章的信息''' coon = MongoClient(host=self.host, port=self.port) coll = coon[self.db_name][self.coll_name] coll.insert(entry)
保存到数据库的函数须要传入一个参数,是一个 dict
类型,这个参数正好能够理解为提取的单个文章的信息。
首先,按照 mongodb
的链接方式创建一个链接,这个链接就使用到了建立类的时候传入的数据库的参数,而后使用 insert()
方法就能够插入数据了。
讲过上述的的函数,已经能够提取总页码数,也能够提取并保存单个页码的文章信息了,剩下的就是使用循环来提取全部页码的信息了,因而,能够把这个过程写到一个主函数中:
def main(self): '''主函数,循环迭代进行翻页,提取全部页码的信息并保存到数据库''' total_pages = int(self.get_total_pages()) for i in range(1, total_pages + 1): self.get_infos(i) print('总计{}页,已经爬完{}页'.format(total_pages, i))
为了方便查看爬虫的进度,在每爬取一页信息,能够打印当前的进度。
最后一段代码就是运行爬虫,首先给定一些须要传递到爬虫类中的参数,而后启动爬虫的主程序便可看到爬虫结果。
“Python”这个字段在简书中有100页的数据(若是没有猜错,这个100页应该是简书默认提供的最大页码数),因此爬虫运行的时间并不长,爬完以后可使用可视化工具看看数据库中的信息。
数据库中信息如图:
后记:从数据库中能够查看到一些有效信息,包括文字的标题、连接、发布时间、做者的信息、评论数、阅览量、喜欢数等。虽然这些数据并无什么研究价值,可是本着练手的想法,后续会使用 Python 基本的数据分析工具来对爬取的信息进行可视化分析。