我正在尝试执行以下操作:
- 从某个页面中抓取一些项目(包括URL(
- 将每个项目保存到mysql数据库
- 获取先前添加的记录的(auto_increment(ID
- 抓取上面URL指向的页面(#1(
- 使用对#3中定义的ID的引用(外键(保存此页面上的所有项目
在我看来,这是一个非常常见的问题,应该有一个相当简单的解决方案。
";item_scraped";信号看起来像是一个显而易见的解决方案,因为它提供了一个回调,用于当一个项目被完全处理时。
但是,似乎不可能从回调中启动另一个请求。
我在这里读到一些人建议使用Spider中间件,但我不知道如何使用,尤其是因为Spider中间件不应该自己启动请求。
我在Python 3.8上使用Scrapy 2.6.2。
谢谢你的帮助。
我的简化代码如下,没有错误处理等
蜘蛛
import scrapy
from scrapy import signals
class MySpider(scrapy.Spider):
name = 'MySpider'
allowed_domains = ['www.somedomain.com']
start_urls = [
'https://www.somedomain.com/someurl/',
]
@classmethod
def from_crawler(cls, crawler, *args, **kwargs):
spider = cls()
# requests the callback once an item is scraped
crawler.signals.connect(spider.item_scraped, signals.item_scraped)
return spider
def parse(self, response):
for node in response.xpath(".//tr/td[@class='foo']"):
yield {
'a': ...
'b': ...
'url': ...
}
def item_scraped(self, item, response, spider):
###
### UNFORTUNATELY THIS IS NOT POSSIBLE
###
# forwards the new ID as a meta variable
yield Request(item['url'], meta={'id':item['id']}, callback=self.parse_page)
def parse_page(self, response):
for node in response.xpath("//div[@class='soemthing']//a"):
yield {
'id': response.meta['id'],
'title': ...
}
和管道:
import mysql.connector
class WinebotPipeline(object):
def __init__(self):
self.conf = {'host': ..., 'user': ..., 'password': ..., 'database': ...}
self.connection = self.mysql_connect()
self.cursor = self.connection.cursor()
def mysql_connect(self):
return mysql.connector.connect(**self.conf)
def process_item(self, item, spider):
sql = "insert into `some_table` (`a`, `b`, `url`) values (%s, %s, %s)"
self.cursor.execute(sql, list(item.values()))
self.connection.commit()
# id of last inserted item
item['id'] = self.cursor.lastrowid
return item
一种更简单的方法是从第一个页面获取所有信息,然后使用url生成一个新的请求,parse_page
方法作为回调,刮取的数据作为回调kwargs。然后,一旦你从第二个url中抓取了必要的信息,你就可以将其添加到第一个页面的结果中,并生成完整的项目。然后,您可以一次完成所有数据库条目。
蜘蛛
import scrapy
from scrapy import signals
class MySpider(scrapy.Spider):
name = 'MySpider'
allowed_domains = ['www.somedomain.com']
start_urls = [
'https://www.somedomain.com/someurl/',
]
def parse(self, response):
for node in response.xpath(".//tr/td[@class='foo']"):
cb_kwargs = {'a': ..., 'b': ..., 'url': ...}
yield scrapy.Request(cb_kwargs['url'],
callback=self.parse_page,
cb_kwargs=cb_kwargs)
def parse_page(self, response, **kwargs):
for node in response.xpath("//div[@class='soemthing']//a"):
kwargs.update({'title': ...})
yield kwargs
管道
import mysql.connector
class WinebotPipeline(object):
def __init__(self):
self.conf = {'host': ..., 'user': ..., 'password': ..., 'database': ...}
self.connection = self.mysql_connect()
self.cursor = self.connection.cursor()
def mysql_connect(self):
return mysql.connector.connect(**self.conf)
def process_item(self, item, spider):
sql = "insert into `some_table` (`a`, `b`, `url`, 'title') values (%s, %s, %s, %s)"
self.cursor.execute(sql, list(item.values()))
self.connection.commit()
return item