【问题标题】:How to scrape RottenTomatoes Audience Reviews using Python?如何使用 Python 抓取 RottenTomatoes 观众评论?
【发布时间】:2020-06-15 10:54:39
【问题描述】:

我正在创建一个蜘蛛,使用 scrapy 从 rottentomatoes.com 抓取详细信息。由于搜索页面是动态呈现的,我使用 rottentomatoes API for eg:https://www.rottentomatoes.com/api/private/v2.0/search?q=inception 来获取搜索结果和 URL。通过scrapy的URL,我能够提取番茄计分、观众分数、导演、演员等。但是,我也想提取所有观众评论。问题是,观众评论页面(https://www.rottentomatoes.com/m/inception/reviews?type=user)使用分页工作,我无法从下一页提取数据,而且我也找不到使用 API 提取详细信息的方法。谁能帮我解决这个问题。

    def parseRottenDetail(self, response):
    print("Reached Tomato Parser")
    try:
        if MoviecrawlSpider.current_parse <= MoviecrawlSpider.total_results:
            items = TomatoCrawlerItem()
            MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse]['tomatometerScore'] = response.css(
                '.mop-ratings-wrap__row .mop-ratings-wrap__half .mop-ratings-wrap__percentage::text').get().strip()
            MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse][
                'tomatoAudienceScore'] = response.css(
                '.mop-ratings-wrap__row .mop-ratings-wrap__half.audience-score .mop-ratings-wrap__percentage::text').get().strip()
            MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse][
                'tomatoCriticConsensus'] = response.css('p.mop-ratings-wrap__text--concensus::text').get()
            if MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse]["type"] == "Movie":
                MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse]['Director'] = response.xpath(
                    "//ul[@class='content-meta info']/li[@class='meta-row clearfix']/div[contains(text(),'Directed By')]/../div[@class='meta-value']/a/text()").get()
            else:
                MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse]['Director'] = response.xpath(
                    "//div[@class='tv-series__series-info-castCrew']/div/span[contains(text(),'Creator')]/../a/text()").get()
            reviews_page = response.css('div.mop-audience-reviews__view-all a[href*="reviews"]::attr(href)').get()
            if len(reviews_page) != 0:
                yield response.follow(reviews_page, callback=self.parseRottenReviews)
            else:
                for key in MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse].keys():
                    if "pageURL" not in key and "type" not in key:
                        items[key] = MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse][key]
                yield items
                if MoviecrawlSpider.current_parse <= MoviecrawlSpider.total_results:
                    MoviecrawlSpider.current_parse += 1
                    print("Parse Values are Current Parse " + str(
                        MoviecrawlSpider.current_parse) + "and Total Results " + str(MoviecrawlSpider.total_results))
                    yield response.follow(MoviecrawlSpider.parse_rotten_list[MoviecrawlSpider.current_parse]["pageURL"],
                                          callback=self.parseRottenDetail)
    except Exception as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        print(e)
        print(exc_tb.tb_lineno)

执行这段代码后,我到达评论页面,例如:https://www.rottentomatoes.com/m/inception/reviews?type=user,此后有一个下一步按钮,使用分页加载下一页。那么提取所有评论的方法应该是什么?

    def parseRottenReviews(self, response):
    print("Reached Rotten Review Parser")
    items = TomatoCrawlerItem()

【问题讨论】:

  • 真的很有帮助,非常感谢:D

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


【解决方案1】:

当您转到下一页时,您会注意到它使用了页面的上一个结束光标值。您可以将endCursor 设置为第一次迭代的空字符串。另请注意,您需要 movieId 来请求评论,这个 id 可以从 JS 嵌入的 json 中提取:

import requests
import re
import json

r = requests.get("https://www.rottentomatoes.com/m/inception/reviews?type=user")
data = json.loads(re.search('movieReview\s=\s(.*);', r.text).group(1))

movieId = data["movieId"]

def getReviews(endCursor):
    r = requests.get(f"https://www.rottentomatoes.com/napi/movie/{movieId}/reviews/user",
    params = {
        "direction": "next",
        "endCursor": endCursor,
        "startCursor": ""
    })
    return r.json()

reviews = []
result = {}
for i in range(0, 5):
    print(f"[{i}] request review")
    result = getReviews(result["pageInfo"]["endCursor"] if i != 0  else "")
    reviews.extend([t for t in result["reviews"]])

print(reviews)
print(f"got {len(reviews)} reviews")

请注意,您也可以为第一次迭代刮取 html

【讨论】:

  • 我昨天尝试了这段代码并且工作得很好,但是今天它抛出了一个错误:“'NoneType' object has no attribute 'group'”。这可能是什么原因?是不是烂番茄阻止了我的请求?
【解决方案2】:

当我使用 Scrapy 时,我一直在寻找一种不使用 requests 模块来执行此操作的方法。方法是一样的,但是我发现页面https://www.rottentomatoes.com/m/inception&lt;script&gt;标签中有一个对象root.RottenTomatoes.context.fandangoData,它有一个键“emsId”,它具有传递给api获取的电影的ID细节。浏览每个分页事件的网络选项卡,我意识到他们使用 startCursor 和 endCursor 来过滤每个页面的结果。

pattern = r'\broot.RottenTomatoes.context.fandangoData\s*=\s*(\{.*?\})\s*;\s*\n'
                    json_data = response.css('script::text').re_first(pattern)
                    movie_id = json.loads(json_data)["emsId"]
{SpiderClass}.movieId = movie_id
    next_page = "https://www.rottentomatoes.com/napi/movie/" + movie_id + "/reviews/user?direction=next&endCursor=&startCursor="
                    yield response.follow(next_page, callback=self.parseRottenReviews)

对于第一次迭代,您可以将 startCursorendCursor 参数留空。现在你进入解析函数。您可以从当前响应中获取下一页的startCursorendCursor 参数。重复此操作,直到 hasNextPage 属性为 false。

def parseRottenReviews(self, response):
print("Reached Rotten Review Parser")
current_result = json.loads(response.text)
for review in current_result["reviews"]:
    {SpiderClass}.reviews.append(review) #Spider class memeber So that it could be shared among iterations
if current_result["pageInfo"]["hasNextPage"] is True:
    next_page = "https://www.rottentomatoes.com/napi/movie/" + \
                str({SpiderClass}.movieId) + "/reviews/user?direction=next&endCursor=" + str(
        current_result["pageInfo"][
            "endCursor"]) + "&startCursor=" + str(current_result["pageInfo"]["startCursor"])
    yield response.follow(next_page, callback=self.parseRottenReviews)

现在{SpiderClass}.reviews 数组将包含评论

【讨论】:

    猜你喜欢
    • 2022-01-10
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多