在网络爬虫中,运用Scrapy和Selenium相结合是获取动态网页数据的有用办法。本文将介绍怎么运用Scrapy和Selenium构建一个爬取携程旅行信息的爬虫,完结主动化获取数据的过程。
本文已对部分关键URL进行处理,本文内容仅供参阅,请勿用以任何商业、违法行径

简介

携程(you.ctrip.com)是一个提供旅行信息的网站,但它的部分内容可能是动态加载的,难以直接经过Scrapy获取。这时就需求借助Selenium这样的东西,模仿浏览器行为进行数据的获取和处理。

东西准备

完结过程

  1. 设置Scrapy项目: 创立Scrapy项目并装备爬虫。
  2. 编写爬虫: 运用Scrapy的Spider编写爬虫,设置起始URL和数据提取规矩。
  3. 设置Selenium中间件: 创立Selenium中间件,用于处理需求动态加载的页面内容。
  4. 运用Selenium模仿浏览器行为: 在Selenium中间件中,运用ChromeDriver发动浏览器,模仿点击、等候页面加载等操作。
  5. 处理页面内容: 运用Selenium获取到的页面内容,提取需求的信息并回来给Spider。
  6. 数据存储或处理: Spider获取到数据后,能够选择存储到数据库或进行其他处理。

代码完结

爬虫部分

  1. 爬虫发动

    • 爬虫发动后,读取Excel文件中的景区称号作为查找关键词。
    • 构建对应的携程查找链接,并发起Request恳求。

def start_requests(self):
    df = pd.read_excel("D:codeScrapyscrapy_tourA级景区(按省份).xlsx")
    scenic_namelist = df['景区称号']
    dflen = len(scenic_namelist)
    for i in range(10641, dflen):
        key = scenic_namelist[i]
        newurl = '' + key
        yield Request(url=newurl, meta={'use_selenium': True, 'title': key, 'id': i, 'closeid': dflen - 1})
  1. Selenium装备

    • 经过Selenium进行浏览器模仿,创立Chrome实例,设置headless模式(无界面运转)。
    • 运用预设的Chrome浏览器驱动(chromedriver.exe),翻开携程主页,读取并加载已保存的Cookie信息,完结主动登录。

def creat_browser(self):
    # ... ChromeOptions设置及浏览器实例化 ...
    browser = webdriver.Chrome(service=service, options=options)
    browser.get("")
    browser.delete_all_cookies()
    with open('scrapy_tour/cookies_xiecheng.json', 'r', encoding='utf-8') as f:
        listCookies = json.loads(f.read())
    for cookie in listCookies:
        browser.add_cookie(cookie)
    browser.refresh()
    return browser
  1. 谈论信息收集

    • 在页面加载结束后,运用Selenium定位和等候元素加载,获取谈论相关内容(评分、谈论内容、谈论时刻等)。
    • 完结翻页操作,模仿用户点击下一页,继续获取更多谈论信息,直至到达设定的页数或无法继续翻页。

def parse(self, response):
    # ...
    while True:
        # 定位谈论元素,等候加载
        elements = WebDriverWait(self.driver, 3).until(
            lambda x: x.find_elements(by=By.CSS_SELECTOR, value='.commentList .commentItem .contentInfo'))
        # 获取谈论相关信息
        # 翻页操作
    # ...
  1. 数据存储

    • 将获取的谈论信息存储到XiechengItem中,并运用Scrapy结构的Item Pipeline进行后续处理和存储。

if id is not None and title is not None and commentstr!='':
    xiecheng_item['Title'] = title
    xiecheng_item['Commentlist'] = commentstr
    xiecheng_item['AverageScore'] = averagescore
    xiecheng_item['OpenTime'] = time
    xiecheng_item['Number'] = number
    xiecheng_item['Id'] = id
    yield xiecheng_item

经过这样的爬取办法,能够获取携程上景区的谈论信息,包括评分、谈论内容、谈论时刻等,为进一步剖析景区口碑提供了数据支撑。

中间件

class xiecheng_SeleniumMiddleware:
    def __init__(self):
        self.driver = creat_browser()
        self.winflag = 0
   # 开释资源
    def closemidd(self,request):
        if request.meta.get('closeid')==request.meta.get('id'):
            self.driver.quit()
    def process_request(self, request, spider):
        if request.meta.get('use_selenium'):
            self.driver.get(request.url)
            # 在这里运用Selenium进行页面交互,如点击按钮、填写表单等
            # 并等候页面加载完结
            # 获取页面内容
            # page_source = self.driver.page_source
            # 转换为字节格局,以避免一些编码过错
            # self.driver.implicitly_wait(5)  # 设置隐式等候时刻为5秒
            try:
                # 显现等候保证能找到元素,显现等候3s
                # raise IgnoreRequest("强制取消")
                elements = WebDriverWait(self.driver, 3).until(
                    lambda x: x.find_elements(by=By.CSS_SELECTOR, value='.guide-main-item-bottom .title'))
                Similarity_score = []
                for element in elements:
                    title = element.text
                    oldtitle = request.url.split('=')[1]
                    # url 转码中文
                    oldtitle = urllib.parse.unquote(oldtitle)
                    Similarity_score.append(get_similarity(oldtitle, title))
                    # if Similarity_score[-1][4] >=50:
                    #     print(Similarity_score[-1])
                max_score = None
                max_index = None
                if Similarity_score!=[]:
                    for index, score in enumerate(Similarity_score):
                        if max_score == None or max_score[-1] < score[-1]:
                            max_score = score
                            max_index = index
                # 找到最匹配的选项
                # print('max', max_score)
                # print(max_index)
                # 若成功找到最匹配项,且各种匹配办法得分都大于50.点击该景点获取url
                if max_score != None and max_score[2] >= 50 and max_score[3] >= 50 and max_score[4] >= 50:
                    print('max', max_score)
                    elements[max_index].click()
                    print("click yes")
                    # self.winflag+=1
                    # thiswim=self.winflag
                    li = self.driver.window_handles  # 呈现多个窗口,需求切换句柄,先获取句柄列表
                    if len(li)>=2:
                        self.driver.switch_to.window(li[-1])  # 切换句柄
                        # 显现等候热度数据,等候概况页显现结束
                        hot = WebDriverWait(self.driver, 3).until(
                            lambda x: x.find_elements(by=By.CSS_SELECTOR, value='.heatView .heatScoreView .heatScoreText'))
                        # 将概况页信息发送到spider
                        body = to_bytes(self.driver.page_source, encoding='utf-8')
                        print('传入爬虫url')
                        print(self.driver.current_url)
                        # 修正中间件判断参数
                        request.meta['use_selenium'] = False
                        response = HtmlResponse(url=self.driver.current_url, body=body, encoding='utf-8',
                                                request=request)
                        # 封闭窗口句柄减一
                        self.driver.close()
                        # 切换至查找页面窗口
                        if len(li) >= 1:
                            self.driver.switch_to.window(li[0])
                        # self.winflag-=1
                        self.closemidd(request)
                        return response
                else:
                    self.closemidd(request)
                    raise IgnoreRequest("未找到类似度合格的元素")
            except Exception as e:
                raise IgnoreRequest("中间件报错,或可能是显现等候的元素等候超时或是元素不存在。")
                spider.logger.error(f"Error: 中间件报错,{e}")
                # return None
        else:
            print('未进入携程的中间件,被搬运')
            # 不运用 Selenium,直接回来 None,让 Scrapy 运用默认的下载器处理这个恳求
            # pass
            return None

以上是一个用于Scrapy爬虫的中间件,主要功能是经过Selenium模仿浏览器操作,完结页面交互和内容获取。

  • 初始化

    • 在初始化办法中,创立了一个浏览器实例self.driver
    • 设定了一个标志位self.winflag用于盯梢窗口数量。
  • 恳求处理

    • process_request办法处理恳求,当恳求中包含指定的参数use_selenium时,运用Selenium处理恳求。
    • 运用WebDriverWait进行页面元素的显现等候,等候指定元素加载完结。
    • 依据元素内容的类似度进行匹配,点击最匹配的选项,获取相关概况信息。
    • 假如成功找到匹配项且类似度符合要求,切换到概况页,等候概况页数据加载结束,获取页面信息并构造HtmlResponse对象。
    • 封闭概况页窗口,回来HtmlResponse对象,传递给爬虫处理。
    • 若未找到类似度合格的元素或发生异常,则疏忽该恳求,不进行处理。
  • 资源开释

    • closemidd办法用于开释资源,在恳求处理完结后,依据条件判断是否封闭浏览器窗口句柄。

Pipeline管道

在Scrapy结构中,处理数据的管道(Pipeline)起着至关重要的作用。其中,MySQLPipeline是一种常见的数据处理管道,用于将爬取的数据存储到MySQL数据库中。


class MySQLPipeline:
    def __init__(self, mysql_host, mysql_port, mysql_database, mysql_user, mysql_password):
        # 初始化衔接参数和数据库衔接实例
        # ...
    @classmethod
    def from_crawler(cls, crawler):
        # 从爬虫装备中获取数据库衔接参数
        # ...
    def open_connection(self):
        # 手动开启数据库衔接
        # ...
    def open_spider(self, spider):
        # 在爬虫发动时翻开数据库衔接
        # ...
    def close_spider(self, spider):
        # 在爬虫封闭时封闭数据库衔接并提交数据
        # ...
    def process_item(self, item, spider):
        # 处理爬取到的数据,并依据数据类型履行相应的数据库插入操作
        # ...
    def write_data(self, sql):
        # 履行批量数据写入操作
        # ...
  • 初始化:MySQLPipeline类在初始化时接纳MySQL数据库衔接所需的参数,并创立了数据库衔接的实例以及一个用于暂存数据的列表。
  • 从装备中获取参数:经过from_crawler办法从Scrapy的装备中获取MySQL数据库衔接的参数。
  • 数据库衔接管理open_connection办法用于手动开启数据库衔接;open_spider办法在爬虫发动时调用,也用于开启数据库衔接;close_spider办法在爬虫封闭时调用,用于封闭数据库衔接并提交数据到数据库。
  • 数据处理process_item办法依据不同的数据类型,履行相应的数据库插入操作,将数据存储到对应的数据表中。
  • 批量写入数据write_data办法用于履行批量数据写入操作,将暂存的数据列表批量写入数据库表中,并在操作完结后清空数据列表。

这样咱们就成功的构建了一个旅行信息收集爬虫。

注意事项

  • 页面结构改变: 网站的页面结构可能会不定期更改,导致原有的提取规矩失效,需求定期查看和更新提取规矩。
  • 反爬办法: 网站可能有反爬办法,需求注意不要频繁恳求或暴露爬虫行为。

总结

经过Scrapy和Selenium的结合,咱们能够构建一个能够有用获取旅行信息的爬虫。可是需求注意,爬虫在实际应用中需求遵守网站的规矩,避免对网站形成过大压力或触发反爬机制。

以上便是运用Scrapy和Selenium构建旅行信息爬虫的根本流程和完结办法。

(注意:以上代码和过程仅为示例,实际爬虫需依据网站的页面结构和改变进行相应调整和处理。)