多读书多实践,勤思考善领悟

从零开始学习知识图谱 之 一.电影知识图谱构建 1.半结构化数据的获取

本文于1734天之前发表,文中内容可能已经过时。

一. 简介

本文章针对半结构化数据的获取,介绍基于scrapy构建的百度百科爬虫和互动百科爬虫。同时为了练手还根据教程制作了基于BeautifulSoup和urllib2的百度百科爬虫、微信公众号爬虫和虎嗅网爬虫。

目前百度百科爬虫,爬取电影类数据,包含电影22219部,演员13967人。互动百科爬虫, 爬取电影类数据,包含电影13866部,演员5931 人。

本教程的项目代码放在github上,下载地址为《从零开始学习知识图谱》项目源代码

二. 环境准备

1. 操作系统

支持操作系统:windows、macOS、Linux。为了方便大家搭建开发环境,笔者尽可能在windows下构建,系列篇未特意说明时操作系统都是windows。Linux安装可以参考VirtualBox虚拟机安装UbuntuVirtualBox虚拟机安装CentOS8进行安装。

2. jdk

安装参见windows系统安装JDK

3. mysql

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。

MySQL官网安装包,该安装包有安装指引,按步骤完成即可。

如果想安装MySQL绿色版(不建议),需要手工配置,请参考文章MySQL 8.0.16安装教程(windows 64位)

4. Python3

Python是一种跨平台的计算机程序设计语言。是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越来越多被用于独立的、大型项目的开发。

对于初学者和完成普通任务,Python语言是非常简单易用的。连Google都在大规模使用Python,你就不用担心学了会没用。

项目是Python3开发的,建议安装Python3。

Python官网下载地址:https://www.python.org/downloads/ ,该安装包有安装指引,按步骤完成即可。

详细步骤请参考文章新手必看!如何在windows下安装Python(Python入门教程)

检测环境变量,若无按如下配置:

1). 打开环境变量配置

计算机→属性→高级系统设置→高级→环境变量,在系统变量中配置。

2). 配置PYTHON_HOME

新建,变量名PYTHON_HOME,变量值,Python路径,我的路径是C:\my\Python,保存。

3). 配置Path

打开Path变量,在变量值最前加入%PYTHON_HOME%;%PYTHON_HOME%\Scripts; (分号是分隔符,后面有其它值时必须有分号)。

python安装好之后,我们要检测一下是否安装成功,用系统管理员打开命令行工具cmd,输入“python -V”,然后敲回车,如果出现如下信息,则表示我们安装成功了。

1
2
C:\Users\mmm>python -V
Python 3.7.4

5. IDE(PyCharm)

PyCharm是一种Python IDE,带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试、语法高亮、Project管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外,该IDE提供了一些高级功能,以用于支持Django框架下的专业Web开发。

a. 官网Professional(专业版)下载地址:https://www.jetbrains.com/pycharm/download/#section=windows

b. 下载Jetbrains系列产品2019.2.2激活破解补丁jetbrains-agent.jar文件

网盘链接:https://pan.baidu.com/s/17Q5mpAT49scLdhVG5hvBgg 提取码:k6zb

c. 参考pycharm永久激活教程,永久激活。

6. Scrapy

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

Scrapy依赖的库比较多,在安装之前,你需要确保以下库已经安装:wheel、lxml、pyOpenSSL、Twisted、pywin32,先装完,再装Scrapy。使用可阅读文章Scrapy入门简介及Demo
库地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/

安装方式一:

安装wheel

用途:

pip安装固然方便,但有时候会遇到安装失败的问题。wheel和egg都是打包的格式,支持不需要编译或制作的安装过程。wheel现在被认为是Python标准的二进制打包格式。

安装命令:

pip install wheel

注意:如果你是刚刚安装过python并且从没有安装过wheel,你可以直接运行上述命令。但如果你的pip版本不够新,你需要在执行install命令之前更新一下pip,在命令行中输入:python -m pip install –upgrade pip更新pip,再输入安装命令即可。

安装lxml

用途:

python的一个解析库,支持HTML和XML的解析,支持XPath解析方式,而且解析效率非常高。

安装命令:
安装方式一(在线):

pip install lxml

安装方式二(离线):

你可以进入地址
[https://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml]:

去下载lxml,然后用安装.whl文件的方式安装。
pip install lxml‑4.4.1‑cp37‑cp37m‑win_amd64.whl

下面安装都省略离线安装方式。

安装zope.interface

用途:

python本身不提供interface的实现,需要通过第三方扩展库来使用类似interface的功能,一般都是zope.interface。

注意:不安装zope.interface可能会出现pyOpenSSL安装失败。

安装命令:

pip install zope.interface

安装pyOpenSSL

用途:

让python支持SSL通信协议,简单来说就是加密解密等这系列操作。

安装命令:

pip install pyOpenSSL

安装Twisted

用途:

Twisted是用Python实现的基于事件驱动的网络引擎框架,Twisted支持许多常见的传输及应用层协议,包括TCP、UDP、SSL/TLS、HTTP、IMAP、SSH、IRC以及FTP。就像Python一样,Twisted也具有“内置电池”(batteries-included)的特点。Twisted对于其支持的所有协议都带有客户端和服务器实现,同时附带有基于命令行的工具,使得配置和部署产品级的Twisted应用变得非常方便。

详情参见[https://www.cnblogs.com/misswangxing/p/7712318.html]

安装命令:

pip install Twisted

安装pywin32

用途:

python不自带访问Windows API的库,需要下载这个库做支持。

安装命令:

pip install pywin32

#在python安装根路径的Scripts目录下执行
python pywin32_postinstall.py -install

安装Scrapy

安装命令:

命令:pip install scrapy

可能出现的问题

问题:You are using pip version 19.0.3, however version 19.X is available.
解决方法:输入命令python -m pip install -U pip 或 python -m pip install –upgrade pip

模块安装完毕,输入’scrapy -h’, 输出信息如下则表示Scrapy安装成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
C:\Users\mmm>scrapy -h
Scrapy 1.7.3 - no active project

Usage:
scrapy <command> [options] [args]

Available commands:
bench Run quick benchmark test
fetch Fetch a URL using the Scrapy downloader
genspider Generate new spider using pre-defined templates
runspider Run a self-contained spider (without creating a project)
settings Get settings values
shell Interactive scraping console
startproject Create new project
version Print Scrapy version
view Open URL in browser, as seen by Scrapy

[ more ] More commands available when run from project directory

安装方式二:使用Anaconda工具,快速解决系列

其实,你还可以登录Scrapy中文网,使用Anaconda进行安装,这个方式可能更适合初学编程的童鞋。地址如下:[ http://www.scrapyd.cn/doc/124.html ]

验证安装是否成功

方法一:

命令行:pip list

列表中出现了Scrapy,安装成功。

方法二:

命令行:scrapy

正确执行命令,安装成功。

文件的功能

scrapy.cfg:配置文件

spiders:存放你Spider文件,也就是你爬取的py文件

items.py:相当于一个容器,和字典较像

middlewares.py:定义Downloader Middlewares(下载器中间件)和Spider Middlewares(蜘蛛中间件)的实现

pipelines.py:定义Item Pipeline的实现,实现数据的清洗,储存,验证。

settings.py:全局配置

基本使用

a. 创建工程结构

cmd中执行命令:scrapy startproject 工程名

快速打开当前文件夹的dos命令窗口:找到指定文件的文件夹,按住“shift+右键”,选择“在此处打开命令窗口”或“在此处打开Powershell窗口”即可。

1
2
3
4
5
6
7
PS C:\d\mmm\pycharm> scrapy startproject baidu_baike
New Scrapy project 'baidu_baike', using template directory 'c:\my\python\lib\site-packages\scrapy\templates\project', created in:
C:\d\mmm\pycharm\baidu_baike

You can start your first spider with:
cd baidu_baike
scrapy genspider example example.com

b. 创建一个spider(爬虫文件)

cmd中执行命令:scrapy genspider 文件名 要爬取的网址。

在spiders文件夹下创建

1
2
3
PS C:\d\mmm\pycharm\baidu_baike\baidu_baike\spiders> scrapy genspider -t basic baidubaike baike.baidu.com
Created spider 'baidubaike' using template 'basic' in module:
baidu_baike.spiders.baidubaike

生成baidubaike.py的代码

1
2
3
4
5
6
7
8
9
10
11
# -*- coding: utf-8 -*-
import scrapy


class BaidubaikeSpider(scrapy.Spider):
name = 'baidu'
allowed_domains = ['baike.baidu.com']
start_urls = ['http://baike.baidu.com/']

def parse(self, response):
pass

name:是项目的名字

allowed_domains:是允许爬取的域名,比如一些网站有相关链接,域名就和本网站不同,这些就会忽略。

atart_urls:是Spider爬取的网站,定义初始的请求url,可以多个。

parse方法:是Spider的一个方法,在请求start_url后,之后的方法,这个方法是对网页的解析,与提取自己想要的东西。

response参数:是请求网页后返回的内容,也就是你需要解析的网页。

c. 运行

cmd中执行命令:scrapy crawl 项目名

在项目根路径下执行

1
2
3
PS C:\d\mmm\pycharm\baidu_baike> scrapy crawl baidu
2019-09-26 09:45:40 [scrapy.utils.log] INFO: Scrapy 1.7.3 started (bot: baidu_baike)
2019-09-26 09:45:40 [scrapy.utils.log] INFO: Versions: lxml 4.4.1.0, libxml2 2.9.5, cssselect 1.1.0, parsel 1.5.2, w3lib 1.21.0, Twisted 19.7.0, Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)], pyOpenSSL 19.0.0 (OpenSSL 1.1.1c 28 May 2019), cryptography 2.7, Platform Windows-10-10.0.18362-SP0

三. 项目构建

1. Mysql建库

库内包含 演员、电影、电影类型、演员->电影、电影->类型 五张表:

  • 演员 :爬取内容为 ID, 简介, 中文名,外文名,国籍,星座,出生地,出生日期,代表作品,主要成就,经纪公司;
  • 电影 :ID,简介,中文名,外文名,出品时间,出品公司,导演,编剧,类型,主演,片长,上映时间,对白语言,主要成就;
  • 电影类型: 爱情,喜剧,动作,剧情,科幻,恐怖,动画,惊悚,犯罪,冒险,其他;
  • 演员->电影: 演员ID, 电影ID;
  • 电影-> 类型: 电影ID, 类型ID;

与其相对应的建表语句即要求请参考craw_without_spider/mysql/creat_sql.txt文件。 在修改目标库的名称后,即可使用工具MySQL Workbench执行creat_sql.txt创建数据库。

2. 百度百科爬虫

该爬虫对应与crawl 下的baidu_baike 文件夹。该爬虫基于scrapy框架,爬取电影类数据,包含电影22219部,演员13967人,演员电影间联系1942个,电影与类别间联系23238,其中类别为‘其他’的电影有10个。对应数据集可在百度云下载,提取码:60n6。

修改item.py

在安装scrapy 后,可以通过 scrapy startproject baidu_baike 初始化爬虫框架,它的目录结构为:

.:
baidu_baike scrapy.cfg
./baidu_baike:
init.py items.py middlewares.py pipelines.py settings.py spiders
init.pyc items.pyc middlewares.pyc pipelines.pyc settings.pyc
./baidu_baike/spiders:
baidu_baike.py baidu_baike.pyc init.py init.pyc

baidu_baike/目录下的文件是需要我们手动修改的。其中 items.py 对需要爬取的内容进行管理,便于把抓取的内容传递进pipelines进行后期处理。现在我们对 baidu_baike/baidu_baike/item.py进行修改,添加要爬取的项。

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
import scrapy

class BaiduBaikeItem(scrapy.Item):
# define the fields for your item here like: # name = scrapy.Field() # 包含演员相关属性 actor_id = scrapy.Field()
actor_bio = scrapy.Field()
actor_chName = scrapy.Field()
actor_foreName = scrapy.Field()
actor_nationality = scrapy.Field()
actor_constellation = scrapy.Field()
actor_birthPlace = scrapy.Field()
actor_birthDay = scrapy.Field()
actor_repWorks = scrapy.Field()
actor_achiem = scrapy.Field()
actor_brokerage = scrapy.Field()

# 电影相关属性
movie_id = scrapy.Field()
movie_bio = scrapy.Field()
movie_chName = scrapy.Field()
movie_foreName = scrapy.Field()
movie_prodTime = scrapy.Field()
movie_prodCompany = scrapy.Field()
movie_director = scrapy.Field()
movie_screenwriter = scrapy.Field()
movie_genre = scrapy.Field()
movie_star = scrapy.Field()
movie_length = scrapy.Field()
movie_rekeaseTime = scrapy.Field()
movie_language = scrapy.Field()
movie_achiem = scrapy.Field()

在爬虫运行过程中,我们主要爬取电影和演员两类及其对应的各项属性。对于电影->类别 和 演员->电影两个表会在爬取数据后进行建立。

修改 pipelines.py

pipelines.py 用来将爬取的内容存放到MySQL数据库中。类内有初始化init()、处理爬取内容并保存process_item()、关闭数据库close_spider()三个方法。

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function


import pymysql
from pymysql import connections
from baidu_baike import settings

class BaiduBaikePipeline(object):
def __init__(self):
# 初始化并连接到mysql数据库
self.conn = pymysql.connect(
host=settings.HOST_IP,
port=settings.PORT,
user=settings.USER,
passwd=settings.PASSWD,
db=settings.DB_NAME,
charset='utf8mb4',
use_unicode=True
)
self.cursor = self.conn.cursor()

def process_item(self, item, spider):
# process info for actor
actor_chName = str(item['actor_chName']).encode('utf-8')
actor_foreName = str(item['actor_foreName']).encode('utf-8')
movie_chName = str(item['movie_chName']).encode('utf-8')
movie_foreName = str(item['movie_foreName']).encode('utf-8')

if (item['actor_chName'] != None or item['actor_foreName'] != None) and item['movie_chName'] == None:
actor_bio = str(item['actor_bio']).encode('utf-8')
actor_nationality = str(item['actor_nationality']).encode('utf-8')
actor_constellation = str(item['actor_constellation']).encode('utf-8')
actor_birthPlace = str(item['actor_birthPlace']).encode('utf-8')
actor_birthDay = str(item['actor_birthDay']).encode('utf-8')
actor_repWorks = str(item['actor_repWorks']).encode('utf-8')
actor_achiem = str(item['actor_achiem']).encode('utf-8')
actor_brokerage = str(item['actor_brokerage']).encode('utf-8')

self.cursor.execute("SELECT actor_chName FROM actor;")
actorList = self.cursor.fetchall()
if (actor_chName,) not in actorList :
# get the nums of actor_id in table actor
self.cursor.execute("SELECT MAX(actor_id) FROM actor")
result = self.cursor.fetchall()[0]
if None in result:
actor_id = 1
else:
actor_id = result[0] + 1
sql = """
INSERT INTO actor(actor_id, actor_bio, actor_chName, actor_foreName, actor_nationality, actor_constellation, actor_birthPlace, actor_birthDay, actor_repWorks, actor_achiem, actor_brokerage ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
self.cursor.execute(sql, (actor_id, actor_bio, actor_chName, actor_foreName, actor_nationality, actor_constellation, actor_birthPlace, actor_birthDay, actor_repWorks, actor_achiem, actor_brokerage ))
self.conn.commit()
else:
print("#" * 20, "Got a duplict actor!!", actor_chName)
elif (item['movie_chName'] != None or item['movie_foreName'] != None) and item['actor_chName'] == None:
movie_bio = str(item['movie_bio']).encode('utf-8')
movie_prodTime = str(item['movie_prodTime']).encode('utf-8')
movie_prodCompany = str(item['movie_prodCompany']).encode('utf-8')
movie_director = str(item['movie_director']).encode('utf-8')
movie_screenwriter = str(item['movie_screenwriter']).encode('utf-8')
movie_genre = str(item['movie_genre']).encode('utf-8')
movie_star = str(item['movie_star']).encode('utf-8')
movie_length = str(item['movie_length']).encode('utf-8')
movie_rekeaseTime = str(item['movie_rekeaseTime']).encode('utf-8')
movie_language = str(item['movie_language']).encode('utf-8')
movie_achiem = str(item['movie_achiem']).encode('utf-8')

self.cursor.execute("SELECT movie_chName FROM movie;")
movieList = self.cursor.fetchall()
if (movie_chName,) not in movieList :
self.cursor.execute("SELECT MAX(movie_id) FROM movie")
result = self.cursor.fetchall()[0]
if None in result:
movie_id = 1
else:
movie_id = result[0] + 1
sql = """
INSERT INTO movie( movie_id, movie_bio, movie_chName, movie_foreName, movie_prodTime, movie_prodCompany, movie_director, movie_screenwriter, movie_genre, movie_star, movie_length, movie_rekeaseTime, movie_language, movie_achiem ) VALUES ( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
self.cursor.execute(sql, ( movie_id, movie_bio, movie_chName, movie_foreName, movie_prodTime, movie_prodCompany, movie_director, movie_screenwriter, movie_genre, movie_star, movie_length, movie_rekeaseTime, movie_language, movie_achiem ))
self.conn.commit()
else:
print("Got a duplict movie!!", movie_chName)
else:
print("Skip this page because wrong category!! ")
return item
def close_spider(self, spider):
self.conn.close()

修改中间件 middlewares.py

middlewares.py 内包含一些UserAgent 和 代理来防止被封。可以自己搜集把自带的替换掉,也可以直接用我项目内的。

修改 settings.py

settings.py 包含了爬虫相关的设置,通常我们需要修改我们自定义的pipelines、中间件、随机延迟等信息。这里只需要注意的是,使用爬虫时最好设置一些延迟,尤其目标网站较小时。

编写 baidu_baike.py

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python
# coding=utf-8


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import scrapy
from baidu_baike.items import BaiduBaikeItem
from bs4 import BeautifulSoup
import re
import urllib


class BaiduBaikeSpider(scrapy.Spider, object):
# 定义爬虫名称
name = 'baidu'
# 设置允许的域,不以这个开头的链接不会爬取
allowed_domains = ["baike.baidu.com"]
# 爬虫开始的的网址
start_urls = ['https://baike.baidu.com/item/%E5%88%98%E5%BE%B7%E5%8D%8E/114923']

#start_urls = ['https://baike.baidu.com/item/%E4%B8%83%E5%B0%8F%E7%A6%8F']

# 将返回的标签列表提取文本并返回
def _get_from_findall(self, tag_list):
result = []

for slist in tag_list:
tmp = slist.get_text()
result.append(tmp)
return result

# 程序的核心,可以获取页面内的指定信息,并获取页面内的所有链接做进一步的爬取
# response 是初始网址的返回
def parse(self, response):
# 分析 response来提取出页面最下部的标签信息,如果包含演员或电影则进行爬取,否则跳过
page_category = response.xpath("//dd[@id='open-tag-item']/span[@class='taglist']/text()").extract()
page_category = [l.strip() for l in page_category]
item = BaiduBaikeItem()

# tooooo ugly,,,, but can not use defaultdict
for sub_item in ['actor_bio', 'actor_chName', 'actor_foreName', 'actor_nationality', 'actor_constellation',
'actor_birthPlace', 'actor_birthDay', 'actor_repWorks', 'actor_achiem', 'actor_brokerage',
'movie_bio', 'movie_chName', 'movie_foreName', 'movie_prodTime', 'movie_prodCompany',
'movie_director', 'movie_screenwriter', 'movie_genre', 'movie_star', 'movie_length',
'movie_rekeaseTime', 'movie_language', 'movie_achiem']:
item[sub_item] = None

# 如果包含演员标签则认为是演员
if u'演员' in page_category:
print("Get a actor page")
soup = BeautifulSoup(response.text, 'lxml')
summary_node = soup.find("div", class_="lemma-summary")
item['actor_bio'] = summary_node.get_text().replace("\n", " ")

# 使用 bs4 对页面内信息进行提取并保存到对应的item内
all_basicInfo_Item = soup.find_all("dt", class_="basicInfo-item name")
basic_item = self._get_from_findall(all_basicInfo_Item)
basic_item = [s.strip() for s in basic_item]
all_basicInfo_value = soup.find_all("dd", class_="basicInfo-item value")
basic_value = self._get_from_findall(all_basicInfo_value)
basic_value = [s.strip() for s in basic_value]
for i, info in enumerate(basic_item):
info = info.replace(u"\xa0", "")
if info == u'中文名':
item['actor_chName'] = basic_value[i]
elif info == u'外文名':
item['actor_foreName'] = basic_value[i]
elif info == u'国籍':
item['actor_nationality'] = basic_value[i]
elif info == u'星座':
item['actor_constellation'] = basic_value[i]
elif info == u'出生地':
item['actor_birthPlace'] = basic_value[i]
elif info == u'出生日期':
item['actor_birthDay'] = basic_value[i]
elif info == u'代表作品':
item['actor_repWorks'] = basic_value[i]
elif info == u'主要成就':
item['actor_achiem'] = basic_value[i]
elif info == u'经纪公司':
item['actor_brokerage'] = basic_value[i]
yield item
elif u'电影' in page_category:
print("Get a movie page!!")

# 使用 bs4 对页面内的链接进行提取,而后进行循环爬取
soup = BeautifulSoup(response.text, 'lxml')
summary_node = soup.find("div", class_="lemma-summary")
item['movie_bio'] = summary_node.get_text().replace("\n", " ")
all_basicInfo_Item = soup.find_all("dt", class_="basicInfo-item name")
basic_item = self._get_from_findall(all_basicInfo_Item)
basic_item = [s.strip() for s in basic_item]
all_basicInfo_value = soup.find_all("dd", class_="basicInfo-item value")
basic_value = self._get_from_findall(all_basicInfo_value)
basic_value = [s.strip() for s in basic_value]
for i, info in enumerate(basic_item):
info = info.replace(u"\xa0", "")
if info == u'中文名':
item['movie_chName'] = basic_value[i]
elif info == u'外文名':
item['movie_foreName'] = basic_value[i]
elif info == u'出品时间':
item['movie_prodTime'] = basic_value[i]
elif info == u'出品公司':
item['movie_prodCompany'] = basic_value[i]
elif info == u'导演':
item['movie_director'] = basic_value[i]
elif info == u'编剧':
item['movie_screenwriter'] = basic_value[i]
elif info == u'类型':
item['movie_genre'] = basic_value[i]
elif info == u'主演':
item['movie_star'] = basic_value[i]
elif info == u'片长':
item['movie_length'] = basic_value[i]
elif info == u'上映时间':
item['movie_rekeaseTime'] = basic_value[i]
elif info == u'对白语言':
item['movie_language'] = basic_value[i]
elif info == u'主要成就':
item['movie_achiem'] = basic_value[i]
yield item

soup = BeautifulSoup(response.text, 'lxml')
links = soup.find_all('a', href=re.compile(r"/item/"))
for link in links:
new_url = link["href"]
new_full_url = urllib.parse.urljoin('https://baike.baidu.com/', new_url)
yield scrapy.Request(new_full_url, callback=self.parse)

运行项目

cmd中执行命令:scrapy crawl 项目名(baidu_baike.py中的name值)

在项目根路径下执行

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
PS C:\d\mmm\pycharm\baidu_baike> scrapy crawl baidu
2019-09-26 09:45:40 [scrapy.utils.log] INFO: Scrapy 1.7.3 started (bot: baidu_baike)
2019-09-26 09:45:40 [scrapy.utils.log] INFO: Versions: lxml 4.4.1.0, libxml2 2.9.5, cssselect 1.1.0, parsel 1.5.2, w3lib 1.21.0, Twisted 19.7.0, Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)], pyOpenSSL 19.0.0 (OpenSSL 1.1.1c 28 May 2019), cryptography 2.7, Platform Windows-10-10.0.18362-SP0
2019-09-26 09:45:40 [scrapy.crawler] INFO: Overridden settings: {'BOT_NAME': 'baidu_baike', 'NEWSPIDER_MODULE': 'baidu_baike.spiders', 'SPIDER_MODULES': ['baidu_baike.spiders'], 'USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'}
2019-09-26 09:45:40 [scrapy.extensions.telnet] INFO: Telnet Password: 4e0e039c01ca23b2
2019-09-26 09:45:40 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
'scrapy.extensions.telnet.TelnetConsole',
'scrapy.extensions.logstats.LogStats']
2019-09-26 09:45:41 [scrapy.middleware] INFO: Enabled downloader middlewares:
['baidu_baike.middlewares.RandomUserAgent',
'baidu_baike.middlewares.ProxyMiddleWare',
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
'scrapy.downloadermiddlewares.retry.RetryMiddleware',
'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware',
'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware',
'scrapy.downloadermiddlewares.redirect.RedirectMiddleware',
'scrapy.downloadermiddlewares.cookies.CookiesMiddleware',
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware',
'scrapy.downloadermiddlewares.stats.DownloaderStats']
2019-09-26 09:45:41 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
'scrapy.spidermiddlewares.offsite.OffsiteMiddleware',
'scrapy.spidermiddlewares.referer.RefererMiddleware',
'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
'scrapy.spidermiddlewares.depth.DepthMiddleware']
2019-09-26 09:45:41 [scrapy.middleware] INFO: Enabled item pipelines:
['baidu_baike.pipelines.BaiduBaikePipeline']
2019-09-26 09:45:41 [scrapy.core.engine] INFO: Spider opened
2019-09-26 09:45:41 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2019-09-26 09:45:41 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2019-09-26 09:45:41 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://baike.baidu.com/item/%E5%88%98%E5%BE%B7%E5%8D%8E/114923> (referer: None)
Get a actor page
2019-09-26 09:45:42 [scrapy.core.scraper] DEBUG: Scraped from <200 https://baike.baidu.com/item/%E5%88%98%E5%BE%B7%E5%8D%8E/114923>
......

3. 互动百科爬虫

该爬虫对应与crawl 下的hudong_baike 文件夹。该爬虫基于scrapy框架,爬取电影类数据,包含电影13866部,演员5931人,演员电影间联系800个,电影与类别间联系14558,其中类别为‘其他’的电影有0个。对应数据集可在百度云下载,提取码:pya9

互动百科爬虫的结构和百度百科相同。二者的主要不同之处在于二者的 info box 的格式不一致,因此采用了不同的方法进行提取。此处不再赘述。

四. 总结

本文章对半结构化数据,即百度百科和互动百科做了爬取并保存到数据库中。这样就相当于我们获得了一份结构化的数据。下篇文章将使用直接映射和D2RQ将其转化为三元组的形式。