Scrapy - парсинг элементов, разбитых на страницы
У меня есть url формы:
example.com/foo/bar/page_1.html
В общей сложности 53 страницы, каждая из которых имеет ~ 20 строк.
Я в основном хочу получить все строки со всех страниц, т.е. ~ 53 * 20 элементов.
У меня есть рабочий код в моем методе синтаксического анализа, который анализирует одну страницу, а также выводит одну страницу за каждый элемент, чтобы получить дополнительную информацию об элементе:
def parse(self, response):
hxs = HtmlXPathSelector(response)
restaurants = hxs.select('//*[@id="contenido-resbus"]/table/tr[position()>1]')
for rest in restaurants:
item = DegustaItem()
item['name'] = rest.select('td[2]/a/b/text()').extract()[0]
# some items don't have category associated with them
try:
item['category'] = rest.select('td[3]/a/text()').extract()[0]
except:
item['category'] = ''
item['urbanization'] = rest.select('td[4]/a/text()').extract()[0]
# get profile url
rel_url = rest.select('td[2]/a/@href').extract()[0]
# join with base url since profile url is relative
base_url = get_base_url(response)
follow = urljoin_rfc(base_url,rel_url)
request = Request(follow, callback = parse_profile)
request.meta['item'] = item
return request
def parse_profile(self, response):
item = response.meta['item']
# item['address'] = figure out xpath
return item
Вопрос в том, как я сканирую каждую страницу?
example.com/foo/bar/page_1.html
example.com/foo/bar/page_2.html
example.com/foo/bar/page_3.html
...
...
...
example.com/foo/bar/page_53.html
Ответы
Ответ 1
У вас есть два варианта решения вашей проблемы. Общее состоит в том, чтобы использовать yield
для генерации новых запросов вместо return
. Таким образом, вы можете выдать более одного нового запроса из одного обратного вызова. Проверьте второй пример на http://doc.scrapy.org/en/latest/topics/spiders.html#basespider-example.
В вашем случае есть, вероятно, более простое решение: просто сгенерируйте список стартовых устройств из такого типа:
class MySpider(BaseSpider):
start_urls = ['http://example.com/foo/bar/page_%s.html' % page for page in xrange(1,54)]
Ответ 2
Вы можете использовать CrawlSpider вместо BaseSpider и использовать SgmlLinkExtractor для извлечения страниц в разбивке на страницы.
Например:
start_urls = ["www.example.com/page1"]
rules = ( Rule (SgmlLinkExtractor(restrict_xpaths=('//a[@class="next_page"]',))
, follow= True),
Rule (SgmlLinkExtractor(restrict_xpaths=('//div[@class="foto_imovel"]',))
, callback='parse_call')
)
Первое правило сообщает, что scrapy следует за ссылкой, содержащейся в выражении xpath, второе правило сообщает scrapy о вызове parse_call для ссылок, содержащихся в выражении xpath, в случае, если вы хотите разобрать что-то на каждой странице.
Для получения дополнительной информации см. документ: http://doc.scrapy.org/en/latest/topics/spiders.html#crawlspider
Ответ 3
Для "scrapy - парсинга элементов, разбитых на страницы" могут быть два варианта использования.
А). Мы просто хотим переместиться через таблицу и получить данные. Это относительно прямо.
class TrainSpider(scrapy.Spider):
name = "trip"
start_urls = ['somewebsite']
def parse(self, response):
''' do something with this parser '''
next_page = response.xpath("//a[@class='next_page']/@href").extract_first()
if next_page is not None:
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
Наблюдайте последние 4 строки. Здесь
- Мы получаем следующую ссылку на страницу следующей страницы xpath из кнопки "Следующая".
- если условие, чтобы проверить, не является ли его окончанием разбивки на страницы.
- Присоединитесь к этой ссылке (которую мы получили на шаге 1) с основным URL-адресом, используя URL-соединение.
- Рекурсивный вызов метода обратного вызова
parse
.
B) Мы не только хотим перемещаться по страницам, но также хотим извлечь данные из одной или нескольких ссылок на этой странице.
class StationDetailSpider(CrawlSpider):
name = 'train'
start_urls = [someOtherWebsite]
rules = (
Rule(LinkExtractor(restrict_xpaths="//a[@class='next_page']"), follow=True),
Rule(LinkExtractor(allow=r"/trains/\d+$"), callback='parse_trains')
)
def parse_trains(self, response):
'''do your parsing here'''
Проследите, чтобы:
-
Мы используем подкласс CrawlSpider
родительского класса scrapy.Spider
-
Мы установили "Правила"
a) Первое правило просто проверяет наличие "следующей_страницы" и следует за ним.
b) Второе правило запрашивает все ссылки на странице, находящиеся в формате, например /trains/12343
, а затем вызывает parse_trains
для выполнения и синтаксического анализа.
-
Важно. Обратите внимание: мы не хотим использовать обычный parse
метод здесь, поскольку мы используем подкласс CrawlSpider
. Этот класс также имеет метод parse
, поэтому мы не хотим его переопределять. Просто не забудьте назвать свой метод обратного вызова чем-то другим, кроме parse
.