Scrapy入门

Scrapy 是一个基于 Python 的网络爬虫,可以用来从网站提取信息。它快速简单,可以像浏览器一样浏览页面。

但是,请注意,它不适合使用JavaScript来操纵用户界面的网站和应用程序。 Scrapy只加载HTML。它没有任何设施能够执行网站可以使用来定制用户体验JavaScript。

安装

我们使用Virtualenv来安装scrapy。这使我们能够安装scrapy而不影响其他系统安装的模块。

现在创建一个工作目录并在该目录中初始化一个虚拟环境。

mkdir working
cd working
virtualenv venv
. venv/bin/activate

现在安装Scrapy

pip install scrapy

检查它正在工作。以下代码显示将scrapy的版本显示为1.4.0。

scrapy
# prints
Scrapy 1.4.0 - no active project
 
Usage:
  scrapy <command></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)
...

编写一个Spider

Scrapy的工作原理是加载一个叫做spider的Python模块,它是一个从scrapy.Spider继承而来的类。

让我们来写一个简单的spider类来加载Reddit的顶部帖子。

首先,创建一个名为redditspider.py的文件,并添加以下内容。这是一个完整的spider类,尽管对我们没有任何帮助。一个spider类至少要求如下:

  • 一个name来识别这个spider类
  • 一个start_urls列表变量,包含从哪个URL开始爬行。
  • 一个parse()方法,它可以是无操作的,如图所示
import scrapy
 
class redditspider(scrapy.Spider):
    name = 'reddit'
    start_urls = ['https://www.reddit.com/']
 
    def parse(self, response):
        pass

这个类现在可以执行如下:

scrapy runspider redditspider.py
 
# prints
...
2017-06-16 10:42:34 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2017-06-16 10:42:34 [scrapy.core.engine] INFO: Spider opened
2017-06-16 10:42:34 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
...

关闭日志记录

正如你所看到的,这个Spider运行并打印一大堆消息,这对调试很有用。但是,由于它掩盖了out程序的输出,现在让我们关闭它。

将这几行添加到文件的开头:

import logging
logging.getLogger('scrapy').setLevel(logging.WARNING)

现在,当我们运行Spider,我们不应该看到令人的混淆信息。

解析响应

现在我们来分析一下scraper的反应。这是在parse()方法中完成的。在此方法中,我们使用response.css()方法在HTML上执行CSS样式选择并提取所需的元素。

为了确定要提取的CSS选择,我们使用Chrome的DOM Inspector工具来选取元素。在reddit的首页,我们看到每个帖子都被包装在<div class =“thing”> ... </ div>中。

因此,我们从页面中选择所有的div.thing,并使用它进一步工作。

def parse(self, response):
    for element in response.css('div.thing'):
        pass

我们还在Spider类中实现了下面的辅助方法来提取所需的文本。

以下方法从元素中提取所有文本为列表,用空格连接元素,并从结果中去除前导和后面的空白。

def a(self, response, cssSel):
    return ' '.join(response.css(cssSel).extract()).strip()

这个方法从第一个元素中提取文本并返回。

def f(self, response, cssSel):
    return response.css(cssSel).extract_first()

提取所需的元素

一旦这些辅助方法到位,让我们从每个Reddit帖子中提取标题。在div.thing内,标题在div.entry> p.title> a.title :: text里是能被利用的。如前所述,可以从任何浏览器的DOM Inspector中确定所需元素的CSS选择。

def parse(self, resp):
    for e in resp.css('div.thing'):
        yield {
            'title': self.a(e,'div.entry>p.title>a.title::text'),
        }

结果使用python的yield语句返回给调用者。 yield的工作方式如下 - 执行一个包含yield语句的函数将返回一个生成器给调用者。调用者重复执行该生成器,并接收执行结果直到生成器终止。

在我们的例子中,parse()方法在每个调用中返回一个字典对象,其中包含一个键(标题)给调用者,返回直到div.thing列表结束。

运行Spider并收集输出。

现在让我们再次运行Spider。显示了丰富输出的一部分(在重新设置日志语句之后)。

scrapy runspider redditspider.py
# prints
...
2017-06-16 11:35:27 [scrapy.core.scraper] DEBUG: Scraped from 
{'title': u'The Plight of a Politician'}
2017-06-16 11:35:27 [scrapy.core.scraper] DEBUG: Scraped from 
{'title': u'Elephants foot compared to humans foot'}
...

很难看到真正的产出。让我们将输出重定向到一个文件(posts.json)

scrapy runspider redditspider.py -o posts.json

这里是posts.json的一部分

...
{"title": "They got fit together"},
{"title": "Not all heroes wear capes"},
{"title": "This sub"},
{"title": "So I picked this up at a flea market.."},
...

提取所有必需的信息

我们还要提取每个帖子的subreddit名称和投票数。为此,我们只更新yield语句返回的结果。

def parse(S, r):
    for e in r.css('div.thing'):
        yield {
            'title': S.a(e,'div.entry>p.title>a.title::text'),
            'votes': S.f(e,'div.score.likes::attr(title)'),
            'subreddit': S.a(e,'div.entry>p.tagline>a.subreddit::text'),
        }

生成的posts.json:

...
{"votes": "28962", "title": "They got fit together", "subreddit": "r/pics"},
{"votes": "6904", "title": "My puppy finally caught his Stub", "subreddit": "r/funny"},
{"votes": "3925", "title": "Reddit, please find this woman who went missing during E3!", "subreddit": "r/NintendoSwitch"},
{"votes": "30079", "title": "Yo-Yo Skills", "subreddit": "r/gifs"},
{"votes": "2379", "title": "For every upvote I won't smoke for a day", "subreddit": "r/stopsmoking"},
...

总结

本文提供了如何从使用Scrapy的网站中提取信息的基本视图。要使用scrapy,我们需要编写一个Spider模块,来指示scrapy抓取一个网站并从中提取结构化的信息。这些信息可以以JSON格式返回,供下游软件使用。

原文链接:https://dzone.com/articles/getting-started-with-scrapy

原文作者:Jay Sridhar

发表于

我来说两句

1 条评论
登录 后参与评论

相关文章

来自专栏小红豆的数据分析

小蛇学python(10)tkinter和pandas的补充

本文主要是想对写界面以及操作表格遇到的常见问题做个总结。前两篇文章想想对tkinter和pandas这两个库的概述还不够全面。

1003
来自专栏数据结构与算法

牛客NOIP普及组R1 C括号(dp)

541
来自专栏阮一峰的网络日志

如何做到 jQuery-free?

jQuery是现在最流行的JavaScript工具库。 据统计,目前全世界57.3%的网站使用它。也就是说,10个网站里面,有6个使用jQuery。如果只考察使...

2944
来自专栏跟着阿笨一起玩NET

winform下Textbox的AutoComplete功能

本文转载:http://blog.csdn.net/xiaoxian8023/article/details/8511129

452
来自专栏ThoughtWorks

前端页面替换文本的方法和一些小技巧

在前端页面替换文本有几种做法,不假思索的答案通常是直接用JavaScript。但你有没有想过这完全可以用CSS实现呢? 背景 在前端页面上,有的时候我们需要...

3067
来自专栏有趣的Python

Scrapy分布式爬虫打造搜索引擎 -(二)伯乐在线爬取所有文章Python分布式爬虫打造搜索引擎

Python分布式爬虫打造搜索引擎 基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站 二、伯乐在线爬取所有文章 ...

3565
来自专栏Java帮帮-微信公众号-技术文章全总结

WAI-ARIA无障碍网页应用属性完全展示

WAI-ARIA指无障碍网页应用。主要针对的是视觉缺陷,失聪,行动不便的残疾人以及假装残疾的测试人员。尤其像盲人,眼睛看不到,其浏览网页则需要借助辅助设备,如屏...

1034
来自专栏liulun

riot.js教程【二】组件撰写准则、预处理器、标签样式和装配方法

基本要求 一个riot标签,就是展现和逻辑的组合(也就是html和JS) 以下是编写riot标签最基本的规则: 先撰写HTML,再撰写JS,JS代码可以写在<s...

2006
来自专栏Hongten

Jxl 简单运用 Excel创建,插入数据,图片,更新数据,

351
来自专栏IMWeb前端团队

移动端重构实战系列2——line list

本文作者:IMWeb 结一 原文出处:IMWeb社区 未经同意,禁止转载 ”本系列教程为实战教程,是本人移动端重构经验及思想的一次总结,也是对sand...

2048

扫码关注云+社区