首先这种网站一定要设置爬取的速率,目标网站用这种方式写入网页估计是被爬虫搞怕了,大概率有更简单的反爬方法,因此爬取速率要注意。博主要爬的网站是一个电影网站:艺恩,点击下一页可以看到其实执行了一个js拿数据,但是URL却没有任何变化,我们需要一路下一页下一页点下去,然后把展示出的电影详情也搞到。
爬取思路:
- 启动selenium,控制Chrome开两个标签页,第一个标签页显示主页,第二个标签页显示不同的电影详情页
- 如果是需要点击下一步的,就先跳转到第一个tab,然后点击“下一步”,把网页信息传回去。
- 如果要显示电影详情页,就跳转到第二个tab,然后把网页信息传回去。
代码:
- 首先是爬虫文件spider的编写
# -*- coding: gbk -*-
from scrapy.spiders import CrawlSpider
import scrapy
from urllib.parse import urljoin
class MovieDetailSpider(CrawlSpider):
name = "flim"
allowed_domains = ["endata"]
def start_requests(self):
start_url = 'https://www.endata/BoxOffice/MovieStock/movies.html'
self.page = 1 # 当前主页的页面
self.max_page = 500 # 预期爬取的最大页面,这个可以往大调整,目的是及时退出循环。
yield scrapy.Request(start_url, self.parse, dont_filter=True, meta={
'page': self.page,
})
def parse(self, response):
# 这里是拿到详情页后的函数
# 1. 提取出这个页面中出现的电影,然后传给movie_detail_page方法提取电影详情页的内容
li_movie_list = response.css('ul.movies-list-box li')
for li_movie_info in li_movie_list[:2]:
relative_url = li_movie_info.css('a::attr(href)').extract_first()
relative_url = relative_url.strip()
movie_url = urljoin(response.url, relative_url)
yield scrapy.Request(movie_url, callback=self.movie_detail_page,
dont_filter=False) # when you run, turn to True
# 重要!这是个 死循环/递归 操作,把详情页的URL继续发送给middleware,回调自己,以方便再次从页面中提取电影列表
start_url = 'https://www.endata/BoxOffice/MovieStock/movies.html'
self.page += 1
if self.page < self.max_page:
yield scrapy.Request(start_url, self.parse, dont_filter=True, meta={
'page': self.page,
})
# next page begin
def movie_detail_page(self, response):
movie_dict = {}
# 这是个提取电影详情页的函数,提取出来内容yield给pipeline保存就行
yield movie_dict
- middleware 文件:
class HandlessMiddleware(object):
def __init__(self):
# 在构造方法里创建Chrome窗口与两个标签页
super(HandlessMiddleware, self).__init__()
option = webdriver.ChromeOptions()
option.add_argument('headless')
prefs = {
"profile.managed_default_content_settings.images": 2, # 禁止加载图片
'permissions.default.stylesheet': 2, # 禁止加载css
}
option.add_experimental_option("prefs", prefs)
self.browser = webdriver.Chrome(chrome_options=option)
self.browser.implicitly_wait(5)
self.browser.execute_script('window.open("","_blank");') # 新建一个标签页
# 每个URL都会被送到这里,所以要根据不同的url判断
# 1. 定位到哪一个标签页
# 2. 是抓取传来的URL还是点击下一页
def process_request(self, request, spider):
# 如果是首页url,意味着要点击“下一页”
if request.url == 'https://www.endata/BoxOffice/MovieStock/movies.html':
# 如果是第一次爬取,那么需要把首页抓下来
if request.meta['page'] == 1:
self.browser.switch_to.window(self.browser.window_handles[0])
self.browser.get(request.url)
self.max_page = int(self.browser.find_element_by_id('TableList_Paging').find_element_by_css_selector(
'a.layui-laypage-last').text) # 这个是首页分了多少页,也就是要点多少次“下一页”
else:
# 如果不是第一次遇到首页url,意味着不需要抓取这个url,而是点击“下一页”
self.browser.switch_to.window(self.browser.window_handles[0])
if request.meta['page'] <= self.max_page:
print("MAIN PAGE CHANGE : " + str(request.meta['page']) + " / " + str(self.max_page))
self.browser.find_element_by_id('TableList_Paging').find_element_by_class_name(
'layui-laypage-next').click() # get next page
else:
return None
else:
# 这是遇到了电影详情页的url,切换到第2个标签页然后得到详情页内容,传回去,传给哪个函数是由第一个文件spider里callback指定的,middleware只需要不择手段拿到网页内容即可。
print("NEW PAGE GET : " + request.url)
self.browser.switch_to.window(self.browser.window_handles[1])
self.browser.get(request.url)
time.sleep(2)
return HtmlResponse(url=self.browser.current_url, body=self.browser.page_source, encoding="utf-8",request=request)
- 在setting中注册
ROBOTSTXT_OBEY = False
COOKIES_ENABLED = False
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UsserAgentMiddleware': None,
'movie_data.middlewares.HandlessMiddleware': 200,
}
更多推荐
scrapy配合selenium爬取需要反复操作同一个动态页面的方法,解决点击“下一页”但是URL相同的网站
发布评论