完整代码
https://github.com/CarryHJR/91dict
需求
爬取人人词典
人人词典最好的地方就是根据单词可以搜到语境,而且有对应的mp3音频,凭直觉这个可能会有版权问题,所以整个爬虫保存下来稳一手
难点:
- 如何解析网页
- 如何保存音频和图片
- 如何设计数据库
- 对于爬虫新手如何写个爬虫
思考
解析网页
一般就是用BeautifulSoup或者xpath语法去解析,两个我都试了一下,xpath对于网页结构稳定的比较好用,不过我试了下91dict.com这个网页对于不同的单词还是有不同的地方,用BeautifulSoup感觉就很稳
文件保存
查看手册https://docs.scrapy.org/en/latest/topics/media-pipeline.html提供了一个用于下载的pipline,只要配置一下settings.json就可以,而且文件名是对文件的hash值,具有唯一性,都不用考虑命令的问题了,本来还想着用时间戳或者uuid来搞
设计数据库
一个单词对应多个item,一个item包含一个图片,一个句子,一个音频,这就是典型的一对多关系,直接设计一个表,列依次是id、图片、单词、音频、句子,这个id感觉这里没有后续拆表的需求,直接用自增id就可以,数据库选择上由于我这里需求很简单没有二次清晰的需求所以直接关系数据库MySQL就可以
create table dict (id int auto_increment primary key, image varchar(255) not null, word varchar(255) not null, audio varchar(255) not null, sentence varchar(255)) DEFAULT CHARSET=utf8;
细节:
- auto_increment 来让id是自增的
- DEFAULT CHARSET=utf8 是因为sentence里面可能会有一些特殊字符
scrapy使用
花了4个小时看了一下scrapy手册,使用scrapy startproject DictSpider
命令生成spider工程,spider文件结构如下
DictSpider/
scrapy.cfg # deploy configuration file
DictSpider/ # project's Python module, you'll import your code from here
__init__.py
items.py # project items definition file
middlewares.py # project middlewares file
pipelines.py # project pipelines file
settings.py # project settings file
spiders/ # a directory where you'll later put your spiders
__init__.py
其中spiders/下文件负责网页解析和逻辑组织,item.py负责封装解析到的信息,比如我这里的相互关联的图片、单词、音频、句子就应该视为一个item,pipelines.py负责对item进行处理,比如插入数据库,settings.py负责总体的配置,比如多个pipline的优先级
实现
搞个虚拟环境先
conda create --name spider python=3.6
conda activate spider
pip install beautifulsoup4
pip install scrapy
pip install PyMySQL
scrapy startproject DictSpider
解析网页
这部分是最容易的,91dict网页里面对应的内容部分刚好有个id="flexslider_2"
,直接用find_all函数就可以迅速定位到,我写了个测试函数test_91dictSpider.py
,通过
scrapy runspider test_91dictSpider.py
当然也可以将结果保存为json
scrapy runspider test_91dictSpider.py -o result.json
设计item和应用pipline
这个部分对于我这个新手是花了最多的时间的,因为一些内部的代码逻辑没搞过的话要全盘理解才能顺畅动手,设计item
file_urls = scrapy.Field()
files = scrapy.Field()
sentence = scrapy.Field()
word = scrapy.Field()
因为根据手册,为了使用默认的下载pipline必须使用加上file_urls和files,另外的实现要点就是自定义一个pipline用来将下载好的文件路径和必要字段插入数据库
# -*- coding: utf-8 -*-
class DictspiderPipeline(object):
def __init__(self):
self.conn = pq.connect(host='localhost', user='root',
passwd='123456', db='renrendict', charset='utf8')
self.cur = self.conn.cursor()
def process_item(self, item, spider):
word = item['word']
sentence = item['sentence']
files = item['files']
image = files[0]['path']
audio = files[1]['path']
sql = "insert into dict(image, word, sentence, audio) VALUES (%s, %s, %s, %s)"
self.cur.execute(sql, (image, word, sentence, audio))
self.conn.commit()
return item
def close_spider(self, spider):
self.cur.close()
self.conn.close()
配置一下settings
FILES_STORE = 'data'
LOG_LEVEL = 'INFO'
ITEM_PIPELINES = {
'scrapy.pipelines.files.FilesPipeline': 1,
'DictSpider.pipelines.DictspiderPipeline': 300,
}
run
cd DictSpider
scrapy crawl 91dict
最终爬取了11万个item,约8G数据
参考
https://www.jianshu.com/p/b29375404479
这里教程涉及了数据库,可惜没有下载item的方式