Scrapy-爬取有道翻译的单词释义

前言

熟悉Scrapy框架后,我们手写第一个爬虫,爬取有道翻译的单词发音,发音文件链接,释义,例句。

需要先熟悉Scrapy框架的同学:点击学习

如单词proportion:有道翻译的详情连接为 http://dict.youdao.com/w/eng/proportion 。本篇文章爬取的内容结果:

1
2
3
4
5
6
7
8
9
10
{"example": [{"en": "I seemed to have lost all sense of proportion.",
"zh": "我好象已经丧失了有关比例的一切感觉。"},
{"en": "The price of this article is out of(all) proportion to its value.",
"zh": "这个商品的价格与它的价值完全不成比例。"},
{"en": "But, the use of interception bases on the violation of the citizen rights, so it should be satisfactory of the principle of legal reservation and the principle of proportion.",
"zh": "但是,监听的适用是以侵害公民权利为前提的,因此监听在刑事侦查中的运用必须满足法律保留原则和比例原则的要求。"}],
"explain": ["n. 比例,占比;部分;面积;均衡", "vt. 使成比例;使均衡;分摊"],
"pron": "[prə'pɔːʃ(ə)n]",
"pron_url": "http://dict.youdao.com/dictvoice?audio=proportion&type=1",
"word": "proportion"}

创建项目

在需要创建的目录下,

1
scrapy startproject youdaoeng

回车即可创建默认的Scrapy项目架构。

创建Item

创建YoudaoengItem继承scrapy.Item,并定义需要存储的单词,发音,发音文件链接,释义,例句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import scrapy


class YoudaoengItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 单词
word = scrapy.Field()
# 英式发音
pron = scrapy.Field()
# 发音audio文件链接
pron_url = scrapy.Field()
# 释义
explain = scrapy.Field()
# 例句
example = scrapy.Field()

创建Spider

spiders目录下创建EngSpider.py,并创建class EngSpider,继承于Spider。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from scrapy import Spider


class EngSpider(Spider):
name = "EngSpider"
# 允许访问的域
allowed_domains = ["dict.youdao.com"]

start_urls = [
'http://dict.youdao.com/w/eng/agree', 'http://dict.youdao.com/w/eng/prophet',
'http://dict.youdao.com/w/eng/proportion']

def parse(self, response):
pass

  • name:用于区别Spider,该名字必须是唯一的。
  • start_urls:Spider在启动时进行爬取的url列表,首先会爬取第一个。
  • def parse(self, response):得到请求url后的response信息的解析方法。

有道翻译的网址为http://dict.youdao.com/ ,根据分析,查询英文单词结果后链接更改,如查询agree,跳转单词详情地址为http://dict.youdao.com/w/eng/agree 。所以几乎可以认为单词的详情页链接可以是http://dict.youdao.com/w/eng/ 拼接上单词本身,所以配置start_urls我们查询三个单词的释义详情。

解析

解析用的Selectors选择器有多种方法:

  • xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表 。
  • css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表.
  • extract(): 序列化该节点为unicode字符串并返回list。
  • re(): 根据传入的正则表达式对数据进行提取,返回unicode字符串list列表。

下面我们用xpath()选择节点,xpath的语法可参考w3c的http://www.w3school.com.cn/xpath/xpath_nodes.asp 学习,需要熟悉语法、运算符、函数等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def parse(self, response):
box = response.xpath('//*[@id="results-contents"]')
word = YoudaoengItem()
# 简明释义
box_simple = box.xpath('.//*[@id="phrsListTab"]')
# 判断查出来的字是否存在
if box_simple:
# 单词
word['word'] = box_simple.xpath('.//h2[@class="wordbook-js"]//span[@class="keyword"]/text()').extract()[0]
# 英式发音
word['pron'] = box_simple.xpath(
'.//h2[@class="wordbook-js"]//div[@class="baav"]//*[@class="phonetic"]/text()').extract()[0]
# 发音链接
word['pron_url'] = "http://dict.youdao.com/dictvoice?audio=" + word['word'] + "&type=1"
# 释义
word['explain'] = []
temp = box_simple.xpath('.//div[@class="trans-container"]//ul//li/text()').extract()
for item in temp:
if len(item) > 0 and not re.search(r'\n', item) and not re.match(r' ', item):
print(item)
word['explain'].append(item)
# 例句
time.sleep(3)
word['example'] = []
example_root = box.xpath('//*[@id="bilingual"]//ul[@class="ol"]/li')
# 1.双语例句是否存在
if example_root:
for li in example_root:
en = ""
for span in li.xpath('./p[1]/span'):
if span.xpath('./text()').extract():
en += span.xpath('./text()').extract()[0]
elif span.xpath('./b/text()').extract():
en += span.xpath('./b/text()').extract()[0]
zh = str().join(li.xpath('./p[2]/span/text()').extract()).replace(' ', '')
word['example'].append(dict(en=en.replace('\"', '\\"'), zh=zh))
# 2.柯林斯英汉双解大辞典的例句是否存在
elif box.xpath('//*[@id="collinsResult"]//ul[@class="ol"]//div[@class="examples"]'):
example_root = box.xpath('//*[@id="collinsResult"]//ul[@class="ol"]//li')
for i in example_root:
if i.xpath('.//*[@class="exampleLists"]'):
en = i.xpath(
'.//*[@class="exampleLists"][1]//div[@class="examples"]/p[1]/text()').extract()[0]
zh = i.xpath(
'.//*[@class="exampleLists"][1]//div[@class="examples"]/p[2]/text()').extract()[0]
word['example'].append(dict(en=en.replace('\"', '\\"'), zh=zh))
if len(word['example']) >= 3:
break
yield word

最后 yield word则是返回解析的word 给Item Pipeline,进行随后的数据过滤或者存储。

运行爬虫-爬取单词释义

运行爬虫,会爬取agree、prophet、proportion三个单词的详情,在项目目录下(scrapy.cfg所在的目录)

1
youdaoeng>scrapy crawl EngSpider -o data.json

即可运行,窗口可以看见爬取的日志内容输出,运行结束后会在项目目录下生成一个data.json文件。


生成的数据为所有item的json格式数组,中文字符都是Unicode编码,可通过一些在线的json解析网站如 https://www.bejson.com/ ,Unicode转中文查看是我们想要的结果。

下载单词语音文件

单词读音的mp3链接为解析时候保存的pron_url字段,接下来我们下载单词mp3文件到本地。
在Item下增加属性pron_save_path,存储发音文件的本地地址:

1
2
# 发音 mp3 本地存放路径
pron_save_path = scrapy.Field()

并在settings.py文件中配置下载文件的目录,如在D:\scrapy_files\目录下,则配置

1
FILES_STORE = "D:\\scrapy_files\\"

增加ItemPipeline重新发起文件下载请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Mp3Pipeline(FilesPipeline):
'''
自定义文件下载管道
'''

def get_media_requests(self, item, info):
'''
根据文件的url发送请求(url跟进)
:param item:
:param info:
:return:
'''
# meta携带的数据可以在response获取到
yield scrapy.Request(url=item['pron_url'], meta={'item': item})

def item_completed(self, results, item, info):
'''
处理请求结果
:param results:
:param item:
:param info:
:return:
'''
file_paths = [x['path'] for ok, x in results if ok]
if not file_paths:
raise DropItem("Item contains no files")

# old_name = FILES_STORE + file_paths[0]
# new_name = FILES_STORE + item['word'] + '.mp3'

# 文件重命名 (相当于剪切)
# os.rename(old_name, new_name)

# item['pron_save_path'] = new_name

# 返回的result是除去FILES_STORE的目录
item['pron_save_path'] = FILES_STORE + file_paths[0]
return item

def file_path(self, request, response=None, info=None):
'''
自定义文件保存路径
默认的保存路径是在FILES_STORE下创建的一个full来存放,如果我们想要直接在FILES_STORE下存放,则需要自定义存放路径。
默认下载的是无后缀的文件,需要增加.mp3后缀
:param request:
:param response:
:param info:
:return:
'''
file_name = request.meta['item']['word'] + ".mp3"
return file_name

需要更改settings.py文件,配置Mp3Pipeline,后面的300为优先级,数字越大,优先级越低。

1
2
3
4
5
6
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
# 'youdaoeng.pipelines.YoudaoengPipeline': 300,
'youdaoeng.pipelines.Mp3Pipeline': 300,
}

运行

1
youdaoeng>scrapy crawl EngSpider -o data1.json

等待运行完成,则在项目目录下生成了data1.json,并在D:\scrapy_files\目录下生成了我们爬取的三个单词的释义。

项目源码

-------------本文结束啦 感谢您的阅读-------------
所谓向日葵族 wechat
💡 更多好文欢迎关注我的公众号~
觉得不错的话,点个赞🌟吧~
0%