首先这种网站一定要设置爬取的速率,目标网站用这种方式写入网页估计是被爬虫搞怕了,大概率有更简单的反爬方法,因此爬取速率要注意。博主要爬的网站是一个电影网站:艺恩,点击下一页可以看到其实执行了一个js拿数据,但是URL却没有任何变化,我们需要一路下一页下一页点下去,然后把展示出的电影详情也搞到。

爬取思路:

  1. 启动selenium,控制Chrome开两个标签页,第一个标签页显示主页,第二个标签页显示不同的电影详情页
  2. 如果是需要点击下一步的,就先跳转到第一个tab,然后点击“下一步”,把网页信息传回去。
  3. 如果要显示电影详情页,就跳转到第二个tab,然后把网页信息传回去。

代码:

  1. 首先是爬虫文件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
  1. 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)
  1. 在setting中注册
ROBOTSTXT_OBEY = False
COOKIES_ENABLED = False

DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.useragent.UsserAgentMiddleware': None,
    'movie_data.middlewares.HandlessMiddleware': 200,
}

更多推荐

scrapy配合selenium爬取需要反复操作同一个动态页面的方法,解决点击“下一页”但是URL相同的网站