使用Scrapy刮擦大量网站

时间:2014-04-09 02:18:34

标签: python web-scraping scrapy

我想分析一些互联网站(例如有关科幻小说的网站)的链接结构和文字内容。 我有一份我想要删除的授权网站列表,其中大约有300个。一旦我在数据库中获得了已爬网页面,我将使用其他工具分析数据。

似乎Scrapy是执行此类任务的最佳工具之一,但我正在努力定义一个执行我需要的蜘蛛。我需要以下功能:

  • 仅搜索某些域(在可能更改的外部文本文件中定义的列表)
  • 递归深度限制为给定值(例如3)。
  • 为每个页面保存标题,html内容和 sql lite db
  • 中的链接
  • 使用缓存以避免锤击网站以下载相同的网页。缓存应具有到期日期(例如1周)。在到期日之后,页面应该再次被删除。
  • 我想手动运行蜘蛛(目前我不需要安排)。

为了实现这一目标,我已经开始以这种方式定义蜘蛛:

# http://doc.scrapy.org/en/latest/intro/tutorial.html

from scrapy.spider import Spider
from scrapy import log
from scrapy.http.request import Request
from scrapy.contrib.spiders import CrawlSpider,Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.selector import HtmlXPathSelector
from scrapy.selector import Selector
from ..items import PageItem

class PageSpider(CrawlSpider):
    name = "page"   

    rules = (Rule(SgmlLinkExtractor(allow=(),), callback='parse_item', follow=True),)   
    #restrict_xpaths=('//body',)), 

    def parse_item(self, response):
        log.msg( "PageSpider.parse" )
        log.msg( response.url )
        #sel = Selector(response)
        links = sel.xpath('//body//a/@href').extract()
        #log.msg("links")
        #log.msg(links)
        item = PageItem()
        item['url'] = response.url
        item['content'] = response.body
        item['links'] = "\n".join( links )
        return item

如何在allow中将允许的网站列表加载到Spider中? 要存储项目,我使用的管道似乎工作正常(它还没有时间逻辑,但它将数据存储在本地数据库中):

# See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html
from scrapy import log
import sqlite3
import time
#from items import PageItem

class MyProjectPipeline(object):

    _db_conn = None;

    def process_item(self, item, spider):
        log.msg( "process item" )
        if not self.url_exists(item['url']):
            # insert element
            c = MyProjectPipeline._db_conn.cursor()
            items = [( item['url'], item['content'], item['links'], time.time() )]
            c.executemany('INSERT INTO pages_dump VALUES (?,?,?,?)', items)
            MyProjectPipeline._db_conn.commit()
        return item

    def open_spider(self, spider):
        # https://docs.python.org/2/library/sqlite3.html
        log.msg( "open sql lite DB" )
        MyProjectPipeline._db_conn = sqlite3.connect('consp_crawl_pages.db')
        c = MyProjectPipeline._db_conn.cursor()
        # create table
        c.execute('''create table if not exists pages_dump ( p_url PRIMARY KEY, p_content, p_links, p_ts )''')
        MyProjectPipeline._db_conn.commit()

    def close_spider(self, spider):
        log.msg( "closing sql lite DB" )
        MyProjectPipeline._db_conn.close()

    def url_exists(self, url):
        c = MyProjectPipeline._db_conn.cursor()
        c.execute("SELECT p_url FROM pages_dump WHERE p_url = ?", (url,))
        data=c.fetchone()
        if data is None:
            return False
        return True

如果db已存在于db?

中,如何阻止蜘蛛请求URL?

我采用了合理的方法,还是在Scrapy中有更自然的方法来做这些事情?我的Python不是很好,所以也欢迎编码建议: - )

感谢您的任何评论, Mulone

1 个答案:

答案 0 :(得分:2)

所以,我意识到这是一个非常晚的答案,但这是我试图回答你的问题:

1)对于抓取txt文件中列出的域,您只需要在__init__方法上填充spider属性allowed_domains

class PageSpider(CrawlSpider):
    name = "page"
    def __init__(self, *args, **kwargs):
        self.allowed_domains = open('YOUR_FILE').read().splitlines()
        super(PageSpider, self).__init__(*args, **kwargs)

2)要限制深度,只需设置DEPTH_LIMIT setting

3)要将数据保存在数据库中,管道是可行的方法 - 您正确地执行它。 =)

4)Scrapy已经通过deault避免了同一次抓取中的重复请求,但为了避免在之前的抓取中发出重复请求,您必须选择一种机制来存储外部先前抓取的请求并过滤{ {3}}就像Talvalin在评论中链接的那样:spider middleware