问题描述
import scrapy
from scrapy_splash import SplashRequest
class Test4basicSpider(scrapy.Spider):
name = 'test4Basic'
allowed_domains = ['letterBoxd.com']
start_urls = ['https://letterBoxd.com/films/popular/size/small/page/1/']
# start_urls = ['https://letterBoxd.com/film/tales-from-the-darkside-the-movie/']
script1 = '''
function main(splash,args)
splash.private_mode_enabled = false
url = args.url
assert(splash:go(args.url))
assert(splash:wait(0.5))
return {
html = splash:html()
}
end
'''
def start_requests(self):
yield SplashRequest(url='https://letterBoxd.com/films/popular/size/small/page/1/',callback=self.parse,endpoint="execute",args={
'lua_source': self.script1
})
def parse(self,response):
for movie in response.xpath("//li[@class='listitem poster-container']"):
movie_url = movie.xpath("(//a[@class='frame'])[1]/@href").get()
yield scrapy.Request(
url=f'https://letterBoxd.com{movie_url}',callback=self.parse_movie,)
next_page = response.xpath("//a[@class='next']/@href").get()
if next_page:
yield SplashRequest(
url=f'https://letterBoxd.com{next_page}',endpoint='execute',args={
'lua_source': self.script1
},callback=self.parse
)
def parse_movie(self,response):
yield {
'title': response.xpath('//section[@id="featured-film-header"]/h1/text()').get(),'year': response.xpath('//small[@class="number"]/a/text()').get(),'duration': response.xpath('(//p[@class="text-link text-footer"]/text())[1]').get(),'genre': response.xpath('//div[@class="text-sluglist capitalize"]/p/a/text()').getall(),'rating': response.xpath('//a[contains(@class,"tooltip display-rating")]/text()').get(),'language': response.xpath('((//span[contains(text(),"Language")]/parent::node()/following::node())/p/a/text())[1]').get()
}
如果我直接从任何电影URL进行刮除,我就能成功地刮除除ratings(需要Javascript)之外的所有字段。我也尝试对分页应用一些逻辑,但是当我尝试爬网时却一无所获。该代码在哪里出错? LetterBoxd的robots.txt文件可以禁止它吗,我不知道如何。
解决方法
关于下一页的问题。听起来好像需要下一页链接的启动请求。您应该考虑如何继续请求下一页链接,直到没有链接为止。
为您提供一些帮助。无需使用飞溅来获得评分。
如果您查看浏览器在检查页面时发出的请求。您已经看到有一个AJAX请求,其中包含与该评分相对应的一些HTML,正如您所建议的那样,它是由javascript加载的。
我倾向于复制此请求并将其粘贴到curl.trillworks.com中。将cURL命令转换为python。然后,您可以处理请求,看看是否可以在没有任何标题的情况下抓住它……
实际上,您甚至不需要标题/参数/ cookie来发出请求。要获取评级信息,您可以向https://letterboxd.com/csi/film/joker-2019/rating-histogram/
代码示例
start_url = ['https://letterboxd.com/csi/film/joker-2019/rating-histogram/']
def parse(self,response):
rating = response.xpath('//a[@class="tooltip display-rating"]/text()').get()
输出
3.8
对于任何电影链接,请将joker-2019替换为指定特定电影页面链接的URL的相应部分。
根据评论更新
您实际上几乎已经掌握了这个。您已经为下一页正确编写了代码。我认为您每个链接的XPATH选择器都有些许错误。
更新代码
for movie in response.xpath("//li[@class='listitem poster-container']"):
movie_url = movie.xpath(".//a[@class='frame']/@href").get()
print(movie_url)
yield scrapy.Request(
url=f'https://letterboxd.com{movie_url}',callback=self.parse_movie,dont_filter=True
)
更正
-
请注意,它应该是
.//
而不是//
。.//
搜索每个response.xpath("//li[@class='listitem poster-container']")
列表项的相对路径。简单的错误,我们都错过了。 -
我不太了解XPATH选择器
'(//a[@class='frame'])[1]/@href'
我已将其更改为有效的
'//a[@class='frame']/@href'
-
它正在过滤所有请求,因为它具有相同的基本URL
letterboxd.com
,因此在scrapy.Request
中,必须确保dont_filter=True
处理所有请求。 / p>
更新2:将Rating纳入代码
请参阅答案的主体,但这是实现。我们创建链接的部分内容,我们需要将其提供给提供评级的直方图URL。然后,我们调用一个回调来获取该评分,然后将该变量通过rating方法传递给parse_movie方法。
def parse(self,response):
for movie in response.xpath("//li[@class='listitem poster-container']"):
movie_url = movie.xpath(".//a[@class='frame']/@href").get()
partial = movie_url.split('/')[-2]
yield scrapy.Request(
url=f'https://letterboxd.com{movie_url}',dont_filter=True
)
rating_url = f'https://letterboxd.com/csi/film/{partial}/rating-histogram/'
yield scrapy.Request(url=rating_url,callback=self.rating)
next_page = response.xpath("//a[@class='next']/@href").get()
if next_page:
yield SplashRequest(
url=f'https://letterboxd.com{next_page}',endpoint='execute',args={
'lua_source': self.script1
},callback=self.parse
)
def rating(self,response):
self.rating = response.xpath('//a[@class="tooltip display-rating"]/text()').get()
def parse_movie(self,response):
yield {
'title': response.xpath('//section[@id="featured-film-header"]/h1/text()').get(),'year': response.xpath('//small[@class="number"]/a/text()').get(),'duration': response.xpath('(//p[@class="text-link text-footer"]/text())[1]').get(),'genre': response.xpath('//div[@class="text-sluglist capitalize"]/p/a/text()').getall(),'rating': self.rating,'language': response.xpath('((//span[contains(text(),"Language")]/parent::node()/following::node())/p/a/text())[1]').get()
}