【问题标题】:Can't parse customized results without using requests within scrapy如果不使用scrapy中的请求,则无法解析自定义结果
【发布时间】:2019-12-01 15:13:42
【问题描述】:

我创建了一个脚本,使用 scrapy 从 imdb.com 获取连接到不同演员姓名的所有链接,然后解析他们的前三个电影链接,最后刮取 directorwriter 的名称那些电影。如果我坚持当前的尝试,我的脚本会完美无缺。但是,我在parse_results 方法中使用了requests 模块(我不想这样做)来获取自定义输出。

website address

脚本的作用(考虑第一个命名链接,如Robert De Niro):

  1. 脚本使用上述 url 并抓取命名链接,以解析位于标题 Filmography 下的 here 的前三个电影链接。

  2. 然后从here解析directorswriters的名称

这是我到目前为止写的(正在工作的):

import scrapy
import requests
from bs4 import BeautifulSoup
from scrapy.crawler import CrawlerProcess

class ImdbSpider(scrapy.Spider):
    name = 'imdb'
    start_urls = ['https://www.imdb.com/list/ls058011111/']

    def parse(self, response):
        soup = BeautifulSoup(response.text,"lxml")
        for name_links in soup.select(".mode-detail")[:10]:
            name = name_links.select_one("h3 > a").get_text(strip=True)
            item_link = response.urljoin(name_links.select_one("h3 > a").get("href"))
            yield scrapy.Request(item_link,meta={"name":name},callback=self.parse_items)

    def parse_items(self,response):
        name = response.meta.get("name")
        soup = BeautifulSoup(response.text,"lxml")
        item_links = [response.urljoin(item.get("href")) for item in soup.select(".filmo-category-section .filmo-row > b > a[href]")[:3]]
        result_list = [i for url in item_links for i in self.parse_results(url)]
        yield {"actor name":name,"associated name list":result_list}

    def parse_results(self,link):
        response = requests.get(link)
        soup = BeautifulSoup(response.text,"lxml")
        try:
            director = soup.select_one("h4:contains('Director') ~ a").get_text(strip=True)
        except Exception as e: director = ""
        try:
            writer = soup.select_one("h4:contains('Writer') ~ a").get_text(strip=True)
        except Exception as e: writer = ""
        return director,writer


c = CrawlerProcess({
    'USER_AGENT': 'Mozilla/5.0',

})
c.crawl(ImdbSpider)
c.start()

输出上述脚本产生的(期望的):

{'actor name': 'Robert De Niro', 'associated name list': ['Jonathan Jakubowicz', 'Jonathan Jakubowicz', '', 'Anthony Thorne', 'Martin Scorsese', 'David Grann']}
{'actor name': 'Sidney Poitier', 'associated name list': ['Gregg Champion', 'Richard Leder', 'Gregg Champion', 'Sterling Anderson', 'Lloyd Kramer', 'Theodore Isaac Rubin']}
{'actor name': 'Daniel Day-Lewis', 'associated name list': ['Paul Thomas Anderson', 'Paul Thomas Anderson', 'Paul Thomas Anderson', 'Paul Thomas Anderson', 'Steven Spielberg', 'Tony Kushner']}
{'actor name': 'Humphrey Bogart', 'associated name list': ['', '', 'Mark Robson', 'Philip Yordan', 'William Wyler', 'Joseph Hayes']}
{'actor name': 'Gregory Peck', 'associated name list': ['', '', 'Arthur Penn', 'Tina Howe', 'Walter C. Miller', 'Peter Stone']}
{'actor name': 'Denzel Washington', 'associated name list': ['Joel Coen', 'Joel Coen', 'John Lee Hancock', 'John Lee Hancock', 'Antoine Fuqua', 'Richard Wenk']}

在上述方法中,我在parse_results 方法中使用了requests 模块来获得所需的输出,因为我无法在任何列表理解中使用yield

如何在不使用requests 的情况下让脚本产生准确的输出?

【问题讨论】:

  • 我不认为有什么好办法。也许忘记将它们分组并在“项目管道”中进行。
  • 在一种情况下,您已经在使用 meta。为什么不使用相同的方法?您将对列表中的第一个 url 产生一个请求,并将该列表与请求元中的其余 URL 一起保留。回调将存储响应中的相关数据,并从列表中生成对下一个 URL 的请求,或者,如果 meta 中的 URL 列表已经为空,则生成包含所有聚合数据的项目。
  • 如果您为这个问题写一个关于您的真正意思的答案@Gallaecio,我将非常高兴。提前非常感谢。
  • @gallacio - 这是一个使用请求绕过的想法,但它仍然一次只做一个,所以它真的违背了目的。
  • 您一次只为一个项目做一个,但如果您的项目多于配置的并发性,这应该不是问题。

标签: python python-3.x web-scraping scrapy


【解决方案1】:

您可以解决此问题的一种方法是使用Request.meta 来保存跨请求项目的待处理 URL 列表,并从中弹出 URL。

正如@pguardiario 所提到的,缺点是您一次仍然只处理该列表中的一个请求。但是,如果您的项目多于配置的并发数,那应该不是问题。

这种方法看起来像这样:

def parse_items(self,response):
    # …
    if item_links:
        meta = {
            "actor name": name,
            "associated name list": [],
            "item_links": item_links,
        }
        yield Request(
            item_links.pop(),
            callback=self.parse_results,
            meta=meta
        )
    else:
        yield {"actor name": name}

def parse_results(self, response):
    # …
    response.meta["associated name list"].append((director, writer))
    if response.meta["item_links"]:
        yield Request(
            response.meta["item_links"].pop(),
            callback=self.parse_results,
            meta=response.meta
        )
    else:
        yield {
            "actor name": response.meta["actor name"],
            "associated name list": response.meta["associated name list"],
        }

【讨论】:

  • 看起来很有希望!!一旦我在脚本中实现它,就回复你。感谢您的时间@Gallaecio。
  • 是的,它按预期工作。你让我很开心@Gallaecio。感谢一万亿。
猜你喜欢
  • 2020-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-10
  • 1970-01-01
  • 1970-01-01
  • 2021-01-06
相关资源
最近更新 更多