Прохождение по нескольким ссылкам на Scrapy

#python #html #web-scraping #xpath #scrapy

Вопрос:

Я пытаюсь сначала просмотреть главную страницу этого веб-сайта в поисках ссылок на таблицу за каждый год. Затем я хотел бы очистить каждый сайт, сохраняя при этом учет каждого года.

До сих пор мой паук был сконструирован как:

 div = response.xpath('//*[@id="sidebar"]/div[1]/nav/ul/li[5]/div')
    
hrefs = div.xpath('*//a').extract()
splits = {}
    
for href in hrefs:
    split = href.split('"')
    link = split[1]
    date = split[2]
    clean_date = "".join(re.findall("[^><a/]",date))
    clean_link = "http://www.ylioppilastutkinto.fi"   str(link)
    splits[clean_date] = clean_link
 

Затем я хотел бы просмотреть каждую ссылку в этом файле и просмотреть их, используя следующую логику:

 table = resp.xpath('//*[@id="content"]/table/tbody')
rows = table.xpath('//tr')
        
data_dict = {"Category": 
            [w3lib.html.remove_tags(num.get()) for num in rows[0].xpath('td')[1:]]
            }

for row in rows[1:]:
    data = row.xpath('td')
    title = w3lib.html.remove_tags(data[0].get())
    nums = [w3lib.html.remove_tags(num.get()) for num in data[1:]]
    data_dict[title] = nums
 

Моя проблема в том, что я не мог найти способ сделать это эффективно. Вызов scrapy.Request по URL — адресу возвращает ответ только с содержимым <html></html> . Если бы существовал способ, при котором объект ответа мог бы напоминать объект, заданный fetch командой в оболочке Scrapy, это было бы идеально, так как я основал логику выбора на тестировании с помощью этой команды.

Редактировать:

Вот весь паук до сих пор

Идея состоит в том, чтобы запустить первый цикл for, чтобы получить ссылку, а затем второй цикл for, чтобы извлечь таблицы из указанных ссылок.

 import scrapy
import regex as re
from scrapy.http import HtmlResponse
import w3lib.html

class MainSpider(scrapy.Spider):
    name = 'links'
    allowed_domains = ['www.ylioppilastutkinto.fi/ylioppilastutkinto/pisterajat']
    start_urls = ['https://www.ylioppilastutkinto.fi/ylioppilastutkinto/pisterajat/']

    def parse(self, response):
        div = response.xpath('//*[@id="sidebar"]/div[1]/nav/ul/li[5]/div')
        
        hrefs = div.xpath('*//a').extract()
        splits = {}
        
        for href in hrefs:
            split = href.split('"')
            link = split[1]
            date = split[2]
            clean_date = "".join(re.findall("[^><a/]",date))
            clean_link = "http://www.ylioppilastutkinto.fi"   str(link)
            splits[clean_date] = clean_link

        
        for date,url in splits.items():
            resp = HtmlResponse(url)
            
            table = resp.xpath('//*[@id="content"]/table/tbody')
            rows = table.xpath('//tr')
        
            data_dict = {"Category":[w3lib.html.remove_tags(num.get()) for num in rows[0].xpath('td')[1:]]}

            for row in rows[1:]:
                data = row.xpath('td')
                title = w3lib.html.remove_tags(data[0].get())
                nums = [w3lib.html.remove_tags(num.get()) for num in data[1:]]
                data_dict[title] = nums
                
        
                yield {
                    'Date': date,
                    'Scores': data_dict}
 

Комментарии:

1. Не ясно, с какого URL вы начинаете? Можете ли вы опубликовать свой полный паук?

2. Что fetch делает, так это использует scrapy.Request для запроса URL-адреса. Можете ли вы показать код паука, который у вас есть до сих пор?

3. Я добавил остальную часть паука.

4. Тебе нужно yield Request … Вы закончили scrapy учебник ?

Ответ №1:

Инициализация a HtmlResponse(url) ничего не дает, так как класс сам не делает запрос.

Чтобы добавить запрос в планировщик scrapy, вам нужно указать один, например: yield scrapy.Request(url, callback=self.parse) .

Тем не менее, есть много улучшений, которые вы можете внести в своего паука.

  • Используйте встроенный scrapy LinkExtractor вместо разделения строк
  • используйте селекторы css вместо жестко закодированных xpath
  • используйте selector.root.text вместо w3lib.remove_tags (чтобы полностью удалить зависимость)

Вот рабочий пример:

 import scrapy
from scrapy.linkextractors import LinkExtractor


class MainSpider(scrapy.Spider):
    name = 'links'
    allowed_domains = ['www.ylioppilastutkinto.fi']
    start_urls = ['https://www.ylioppilastutkinto.fi/ylioppilastutkinto/pisterajat/']

    def parse(self, response):
        le = LinkExtractor(
            allow_domains=self.allowed_domains,
            restrict_xpaths='//*[@id="sidebar"]/div[1]/nav/ul/li[5]/div',
        )
        for link in le.extract_links(response):
            yield scrapy.Request(
                url=link.url,
                callback=self.parse_table,
                cb_kwargs={ 'date': link.text },
            )

    def parse_table(self, response, date):
        rows = response.css('#content table tbody tr')
        if not rows:
            print(f'No table found for url: {response.url}')
            return

        category = [char.root.text for char in rows[0].css('td strong')[1:]]
        if not category:
            category = [char.root.text for char in rows[0].css('td')[1:]]

        for row in rows[1:]:
            cols = row.css('td')
            title = cols[0].root.text
            nums = [col.root.text for col in cols[1:]]
            yield {
                'Date': date,
                'Category': category,
                title: nums
            }
 

Обратите внимание, что ваш анализ категорий, похоже, не работает. Я не совсем уверен, что вы пытаетесь извлечь, поэтому я оставлю это для вас.

Комментарии:

1. Хорошо, потребовалось немного времени, чтобы протестировать и запустить предложенные идеи, и да, все работает хорошо. Спасибо вам за помощь. Также заставил меня провести больше исследований в Scrapy, чтобы улучшить и предыдущих пауков. Кроме того, да, я еще толком не работал над извлечением таблицы, так как моей главной заботой было сначала получить паука на этих сайтах.