使用Scrapy自建数据集
编者按:斯克里普斯研究所数据科学家Michael Galarnyk介绍了如何使用Scrapy爬取网站数据。
我刚开始在业界工作时,首先意识到的一件事情是,有时候需要自己收集、整理、清洗数据。在这篇教程中,我们将从一个众筹网站FundRazr收集数据。和许多网站一样,这个网站有自己的结构、形式,还有众多有用的数据,但却没有一个结构化的API,所以获取数据并不容易。在这篇教程中,我们将爬取网站数据,将其整理为有序的形式,以创建我们自己的数据集。
我们将使用Scrapy,一个构建网页爬虫的框架。Scrapy可以帮助我们创建和维护网页爬虫。它让我们可以专注于使用CSS选择器和XPath表达式提取数据,更少操心爬虫的内部工作机制。这篇教程比Scrapy官方教程要深入一点,希望你在读完这篇教程后,碰到需要抓取有一定难度的数据的情况时,也能自行完成。好了,让我们开始吧。
预备
如果你已经安装了anaconda和google chrome(或Firefox),可以跳过这一节。
安装Anaconda。你可以从官网下载anaconda自行安装,也可以参考我之前写的anaconda安装教程(Mac、Windows、Ubuntu、环境管理)。
安装Scrapy。其实Anaconda已经自带了Scrapy,不过如果遇到问题,你也可以自行安装:
conda install -c conda-forge scrapy
确保你安装了chrome或firefox. 在这篇教程中,我将使用chrome.
创建新Scrapy项目
用
startproject
命令可以创建新项目:该命令会创建一个fundrazr目录:
fundrazr/
scrapy.cfg # 部署配置文件
fundrazr/ # 项目的Python模块
__init__.py
items.py # 项目item定义
pipelines.py # 项目pipeline文件
settings.py # 项目设置文件
spiders/ # 爬虫目录
__init__.py
scrapy startproject fundrazr
使用chrome(或firefox)的开发者工具查找初始url
在爬虫框架中,start_urls
是爬虫开始抓取的url列表。我们将通过start_urls
列表中的每个元素得到单个项目页面的链接。
下图显示,选择的类别不同,初始url也不一样。黑框高亮的部分是待抓取的类别。
在本教程中,start_urls
列表中的第一项是:
https://fundrazr.com/find?category=Health
接下来,我们将看看如何访问下一页,并将相应的url加入start_urls
。
第二个url是:
https://fundrazr.com/find?category=Health&page=2
下面是创建start_urls
列表的代码。其中,npages
指定翻页的页数。
start_urls = ["https://fundrazr.com/find?category=Health"]
npages = 2
for i in range(2, npages + 2 ):
start_urls.append("https://fundrazr.com/find?category=Health&page="+str(i)+"")
使用Srapy shell查找单个项目页面
使用Scrapy shell是学习如何基于Scrapy提取数据的最好方法。我们将使用XPaths,XPaths可以用来选择HTML文档中的元素。
我们首先需要尝试获取单个项目页面链接的XPath。我们将利用浏览器的检查元素。
我们将使用XPath提取下图中红框内的部分。
我们首先启动Scrapy shell:
scrapy shell 'https://fundrazr.com/find?category=Health'
在Scrapy shell中输入以下代码:
response.xpath("//h2[contains(@class, 'title headline-font')]/a[contains(@class, 'campaign-link')]//@href").extract()
使用exit()退出Scrapy shell.
单个项目页面
之前我们介绍了如何提取单个项目页面链接。现在我们将介绍如何提取单个项目页面上的信息。
首先我们前往将要抓取的单个项目页面(链接见下)。
使用上一节提到的方法,检查页面的标题。
现在我们将再次使用Scrapy shell,只不过这次是在单个项目页面启动。
scrapy shell 'https://fundrazr.com/savemyarm'
提取标题的代码是:
response.xpath("//div[contains(@id, 'campaign-title')]/descendant::text()").extract()[0]
页面其他部分同理:
# 筹款总额
response.xpath("//span[contains(@class,'stat')]/span[contains(@class, 'amount-raised')]/descendant::text()").extract()
# 筹款目标
response.xpath("//div[contains(@class, 'stats-primary with-goal')]//span[contains(@class, 'stats-label hidden-phone')]/text()").extract()
# 币种
response.xpath("//div[contains(@class, 'stats-primary with-goal')]/@title").extract()
# 截止日期
response.xpath("//div[contains(@id, 'campaign-stats')]//span[contains(@class,'stats-label hidden-phone')]/span[@class='nowrap']/text()").extract()
# 参与数
response.xpath("//div[contains(@class, 'stats-secondary with-goal')]//span[contains(@class, 'donation-count stat')]/text()").extract()
# 故事
response.xpath("//div[contains(@id, 'full-story')]/descendant::text()").extract()
# url
response.xpath("//meta[@property='og:url']/@content").extract()
Items
网页抓取的主要目标是从无结构的来源提取出结构信息。Scrapy爬虫以Python字典的形式返回提取数据。尽管Python字典既方便又熟悉,但仍然不够结构化:字段名容易出现拼写错误,返回不一致的信息,特别是在有多个爬虫的大型项目中。因此,我们定义Item类来(在输出数据之前)存储数据。
import scrapy
class FundrazrItem(scrapy.Item):
campaignTitle = scrapy.Field()
amountRaised = scrapy.Field()
goal = scrapy.Field()
currencyType = scrapy.Field()
endDate = scrapy.Field()
numberContributors = scrapy.Field()
story = scrapy.Field()
url = scrapy.Field()
将其保存在fundrazr/fundrazr
目录下(覆盖原本的items.py文件)。
爬虫
我们定义爬虫类,供Scrapy使用,以抓取一个网站(或一组网站)的信息。
# 继承scrapy.Spider类
class Fundrazr(scrapy.Spider):
# 指定爬虫名称,运行爬虫时要要到
name = "my_scraper"
# 定义start_urls、npages
# 具体定义见前
def parse(self, response):
for href in response.xpath("//h2[contains(@class, 'title headline-font')]/a[contains(@class, 'campaign-link')]//@href"):
# 加上协议名称
url = "https:" + href.extract()
# 异步抓取
yield scrapy.Request(url, callback=self.parse_dir_contents)
# 回调函数定义
def parse_dir_cntents(self, response):
item = FundrazrItem()
# 下面依次定义item的各字段,
# 具体定义参见前面的XPath表达式
yield item
为了节约篇幅,以上代码仅仅呈现了爬虫的大致结构,省略了导入依赖的语句以及前几节已经涉及的具体代码。完整代码可以从我的GitHub仓库获取:mGalarnyk/Python_Tutorials
爬虫代码保存在fundrazr/spiders
目录下,文件命名为fundrazr_scrape.py
。
运行爬虫
在fundrazr/fundrazr
目录下输入:
scrapy crawl my_scraper -o MonthDay_Year.csv
数据输出文件位于fundrazr/fundrazr
目录下。
我们的数据
输出的数据应该类似下面的图片。由于网站不断地更新,因此具体的众筹项目会不同。另外,项目记录间可能会有空行,这是excel解析csv文件时会出现的现象。
我将npages
从2
改到了450
,并增加了download_delay = 2
,抓取了约6000个项目,保存为MiniMorningScrape.csv
文件。你可以从我的GitHu仓库直接下载这一文件:mGalarnyk/Python_Tutorials
结语
创建数据集可能需要大费周章,而在学习数据科学时却常常被忽略。教程中没有涉及清理数据,我将把这部分内容留给以后的博客文章。如果你有任何问题,请给我留言!
最后,我为这篇教程制作了一个时长约30分钟的配套视频,可以在YouTube上观看:https://youtu.be/O_j3OTXw2_E
如访问YouTube有困难,也可在论智公众号(ID: jqr_AI)后台留言scrapy
,获取视频mp4及英文字幕下载地址。
原文地址:https://towardsdatascience.com/using-scrapy-to-build-your-own-dataset-64ea2d7d4673