#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, чтобы улучшить и предыдущих пауков. Кроме того, да, я еще толком не работал над извлечением таблицы, так как моей главной заботой было сначала получить паука на этих сайтах.