I'm trying to do the following:
- scrape some items (which include URLs) from some page.
- save each item to a mysql database
- get the (auto_increment) ID of the previously added record
- scrape the page pointed to by the URL above (#1)
- save all items on this page with a reference (foreign key) to the ID defined in #3
That looks to me like a very common problem which should have a reasonably simple solution.
The "item_scraped" signal looked like the obvious solution since it provides a callback for when an item has been fully processed.
However, it appears that it's impossible to start another Request from within the callback.
I read here that some people suggested using Spider Middlewares but I don't see how, especially since Spider Middlewares are not supposed to start Requests themselves.
I'm using Scrapy 2.6.2 on Python 3.8.
Thanks for your help.
My simplified code below, without error processing etc.
The spider
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': ...
}
And the pipeline:
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