次のサイト構造があるとします。
- 開始 URL: http://thomas.loc.gov/cgi-bin/query/z?c107:HR。%s : %sは 1 ~ 50 のインデックスです (説明のためのサンプル)。
- 「第 1 層」: 請求書のテキストまたは複数のバージョンへのリンク...
- 「第 2 層」: 「印刷に適した」(プレーン テキスト) バージョンへのリンク付きの請求書テキスト。
スクリプトの最終目標:
- 開始 URL をナビゲートします。URL、タイトル、本文を解析します。それらを starts.txt ファイルに保存する
- 開始 URL の本文から「第 1 レイヤー」リンクを抽出します。これらのリンクに移動します。URL、タイトル、本文を解析します。それらをbills.txtファイルに保存します
- 「第 1 層」の URL の本文から「第 2 層」のリンクを抽出します。これらのリンクに移動します。URL、タイトル、本文を解析します。それらをversions.txtファイルに保存します
次のスクリプトがあるとします。
from scrapy.item import Item, Field
from scrapy.contrib.spiders import CrawlSpider, Rule
from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
from scrapy.selector import HtmlXPathSelector
class StartItem(Item):
url = Field()
title = Field()
body = Field()
class BillItem(Item):
url = Field()
title = Field()
body = Field()
class VersionItem(Item):
url = Field()
title = Field()
body = Field()
class Lrn2CrawlSpider(CrawlSpider):
name = "lrn2crawl"
allowed_domains = ["thomas.loc.gov"]
start_urls = ["http://thomas.loc.gov/cgi-bin/query/z?c107:H.R.%s:" % bill for bill in xrange(000001,00050,00001) ### Sample of 40 bills; Total range of bills is 1-5767
]
rules = (
# Extract links matching /query/D fragment (restricting tho those inside the content body of the url); follow; & scrape all bill text.
# and follow links from them (since no callback means follow=True by default).
# Desired result: scrape all bill text & in the event that there are multiple versions, follow them & parse.
Rule(SgmlLinkExtractor(allow=(r'/query/D'), restrict_xpaths=('//div[@id="content"]')), callback='parse_bills', follow=True),
# Extract links in the body of a bill-version & follow them.
#Desired result: scrape all version text & in the event that there are multiple sections, follow them & parse.
Rule(SgmlLinkExtractor(allow=(r'/query/C'), restrict_xpaths=('//table/tr/td[2]/a/@href')), callback='parse_versions', follow=True)
)
def parse_start_url(self, response):
hxs = HtmlXPathSelector(response)
starts = hxs.select('//div[@id="content"]')
scraped_starts = []
for start in starts:
scraped_start = StartItem() ### Start object defined previously
scraped_start['url'] = response.url
scraped_start['title'] = start.select('//h1/text()').extract()
scraped_start['body'] = response.body
scraped_starts.append(scraped_start)
with open('starts.txt', 'a') as f:
f.write('url: {0}, title: {1}, body: {2}\n'.format(scraped_start['url'], scraped_start['title'], scraped_start['body']))
return scraped_starts
def parse_bills(self, response):
hxs = HtmlXPathSelector(response)
bills = hxs.select('//div[@id="content"]')
scraped_bills = []
for bill in bills:
scraped_bill = BillItem() ### Bill object defined previously
scraped_bill['url'] = response.url
scraped_bill['title'] = bill.select('//h1/text()').extract()
scraped_bill['body'] = response.body
scraped_bills.append(scraped_bill)
with open('bills.txt', 'a') as f:
f.write('url: {0}, title: {1}, body: {2}\n'.format(scraped_bill['url'], scraped_bill['title'], scraped_bill['body']))
return scraped_bills
def parse_versions(self, response):
hxs = HtmlXPathSelector(response)
versions = hxs.select('//div[@id="content"]')
scraped_versions = []
for version in versions:
scraped_version = VersionItem() ### Version object defined previously
scraped_version['url'] = response.url
scraped_version['title'] = version.select('//h1/text()').extract()
scraped_version['body'] = response.body
scraped_versions.append(scraped_version)
with open('versions.txt', 'a') as f:
f.write('url: {0}, title: {1}, body: {2}\n'.format(scraped_version['url'], scraped_version['title'], scraped_version['body']))
return scraped_versions
このスクリプトは、「第 2 レイヤー」リンクへの移動と、これらのサイトの項目 (URL、タイトル、本文) の解析を除いて、私が望むすべてのことを行っているようです。つまり、Scrapy は私の「第 2 レイヤー」をクロールまたは解析していません。
私の質問をもっと簡単に言い直すと: Scrapy が VersionItem に値を入力せず、目的のファイル version.txt に出力しないのはなぜですか?