ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Scrapy中selenium的应用-----并通过京东图书书籍信息爬取项目进行实操!

2021-06-09 19:02:33  阅读:161  来源: 互联网

标签:5.0 Windows Gecko Mozilla selenium 爬取 item Scrapy NT


引言------
在通过scrapy框架进行某些网站数据爬取的时候,往往会碰到页面动态数据加载(ajax)的情况发生,如果直接使用scrapy对其url发请求,是绝对获取不到那部分动态加载出来的数据值。但是通过观察我们会发现,通过浏览器进行url请求发送则会加载出对应的动态加载出的数据。那么如果我们想要在scrapy也获取动态加载出的数据,则必须使用selenium创建浏览器对象,然后通过该浏览器对象进行请求发送,获取动态加载的数据值。

1.案例分析:
需求:爬取京东图书中的所有书籍信息。  (URL为https://book.jd.com/booksort.html)

需求分析:当我们进去该网站时,会发现当前页面展示的书籍分类信息是被动态加载出来的,如果直接通过程序对url进行请求,是获取不到动态加载出来的书籍分类信息的。但是我们发现:如果我们通过selenium进行访问就可以得到所有信息!所以咱们使用selenium实例化一个浏览器对象,在该对象中进行url的请求,获取动态加载的数据。
2.selenium在scrapy中使用的原理分析:

在这里插入图片描述

首先,Scrapy运行流程:

    1.爬虫中起始的url构造的url对象-->爬虫中间件-->引擎-->调度器
    2.调度器把request-->引擎-->下载中间件-->下载器
    3.下载器发送请求,获取response响应--->下载中间件--->引擎-->爬虫中间件--->爬虫
    4.爬虫提取url地址,组装成request对象--->爬虫中间件--->引擎--->调度器,重复步骤2
    5.爬虫提取数据--->引擎--->管道处理和保存数据
爬虫中间件和下载中间件只是运行的逻辑的位置不同,作用是重复的:如替换UA等

然后,分析:

当引擎将京东图书的url对应的请求提交给下载器后,下载器进行网页数据的下载,然后将下载到的页面数据封装到response中,提交给引擎,引擎将response在转交给Spiders。Spiders接受到的response对象中存储的页面数据里是没有动态加载的数据的。要想获取动态加载的数据,则需要在下载中间件中对下载器提交给引擎的response响应对象进行拦截,且对其内部存储的页面数据进行篡改,修改成携带了动态加载出的数据的页面数据,然后将篡改后的response对象最终交给Spiders进行解析操作。

3.selenium在scrapy中的使用流程:
1.重写爬虫文件的构造方法,在该方法中使用selenium实例化一个浏览器对象(因为浏览器对象只需要被实例化一次)。
2.重写爬虫文件的closed(self,spider)方法,在其内部关闭浏览器对象。该方法是在爬虫结束时自动被调用。
3.重写下载中间件的process_response方法,让该方法对响应对象进行拦截,并篡改response中存储的页面数据(使其包含动态加载的数据)。
4.在配置文件中开启下载中间件。
4.实操-------Scrapy结合使用selenium爬取京东图书信息(ajax动态加载的数据)

①创建爬虫项目:

cd到存放此爬虫项目的目录下,输入以下命令:

scrapy startproject jd

②创建爬虫文件:

cd到爬虫项目目录下,输入以下命令:

scrapy genspider book book.jd.com

③编写爬虫文件:(本爬虫文件爬取数据:京东图书所有小分类里第一页60本书籍信息!当然,你可以通过简单的编写实现翻页,以达到获取所有数据的目的!我就不搞了!)

# -*- coding: utf-8 -*-
import json
from selenium import webdriver
import scrapy

from ..items import JdItem


class BookSpider(scrapy.Spider):
    name = 'book_test'
    # 1.检查域名
    allowed_domains = ['book.jd.com']
    # 2.修改起始的url
    start_urls = ['https://book.jd.com/booksort.html']

    def __init__(self):
        # 实例化一个浏览器对象(实例化一次)   开启无头模式
        options = webdriver.ChromeOptions()
        options.add_argument("--headless")
        options.add_argument("--disable-gpu")
        self.driver = webdriver.Chrome(options=options, executable_path="C:\my\Chrome_guge\chromedriver.exe")


    def closed(self, spider):
        # 必须在整个爬虫结束后,关闭浏览器
        print('爬虫结束!')
        self.driver.quit()

    def parse(self, response):
        # 3.获取所有图书大分类节点列表
        big_node_list = response.xpath('//*[@id="booksort"]/div[2]/dl/dt/a')
        for big_node in big_node_list:
            big_category = big_node.xpath('./text()').extract_first()
            big_category_link = response.urljoin(big_node.xpath('./@href').extract_first())
            # 获取所有图书小分类节点列表
            # following-sibling 选取当前节点之后的所有同级节点。   此处:选取当前节点(dt)之后的第一个同级dd节点下的em下的a
            small_node_list = big_node.xpath('../following-sibling::dd[1]/em/a')
            for small_node in small_node_list:
                temp = dict()
                temp['big_category'] = big_category
                temp['big_category_link'] = big_category_link
                temp['small_category'] = small_node.xpath('./text()').extract_first()
                temp['small_category_link'] = response.urljoin(small_node.xpath('./@href').extract_first())
                # 模拟点击小分类链接
                yield scrapy.Request(
                    url=temp['small_category_link'],
                    callback=self.parse_book_link,
                    meta={"py21": temp},
                    dont_filter=True
                )

    def parse_book_link(self, response):
        pass
        # 图书页
        temp = response.meta["py21"]
        book_list = response.xpath('//*[@id="J_goodsList"]/ul/li/div')
        for book in book_list:
            item = JdItem()
            # item.update(temp)         # 试试可以代替下面四句不!
            item['big_category'] = temp['big_category']
            item['big_category_link'] = temp['big_category_link']
            item['small_category'] = temp['small_category']
            item['small_category_link'] = temp['small_category_link']

            item['book_name'] = book.xpath('//*[@id="J_goodsList"]/ul/li/div/div[3]/a/em/text()').extract_first().strip()
            item['book_link'] = 'https://item.jd.com' + book.xpath('//*[@id="J_goodsList"]/ul/li/div/div[3]/a/@href').extract_first()
            item['book_price'] = book.xpath('//*[@id="J_goodsList"]/ul/li/div/div[2]/strong/i/text()').extract_first()
            item['book_pic_link'] = 'https://item.jd.com' + book.xpath('//*[@id="J_goodsList"]/ul/li/div/div[1]/a/img/@src').extract_first()
            print(item)
            yield item

④编写下载中间件文件:

1.因为根据观察即测试可知:京东图书首页书籍分类数据都是动态(ajax)加载的,直接请求获取不到数据,所以对于京东图书首页书籍分类数据的获取(即start_url),我们使用selenium获取!
在这里插入图片描述

2.在观察进入某一书籍分类页面后:根据测试我们会发现,直接请求获取到的数据只有30本书籍的数据,但实际却有60本,可知页面中部分数据也是动态加载的,所以我们对此类页面数据的获取,使用selenium操作,并让页面执行js命令下滑至底部,即可获取完整的60条数据!

在这里插入图片描述

from scrapy.http import HtmlResponse

class JdDownloaderMiddleware(object):
    # Not all methods need to be defined. If a method is not defined,
    # scrapy acts as if the downloader middleware does not modify the
    # passed objects.
	# 只需重写下载中间件的process_response()方法即可:
    def process_response(self, request, response, spider):
        # Called with the response returned from the downloader.
        '''
        参数介绍:
        拦截到响应对象(下载器传递给Spider的响应对象)
        request:响应对象对应的请求对象
        response:拦截到的响应对象
        spider:爬虫文件中对应的爬虫类的实例
        :param request:
        :param response:
        :param spider:
        :return:
        '''
        # 响应对象中存储页面数据的篡改
        if request.url == 'https://book.jd.com/booksort.html':   
            spider.driver.get(url=request.url)
            time.sleep(2)
            # 一定要给予浏览器一定的缓冲加载数据的时间
            # 页面数据就是包含了动态加载出来的数据对应页面数据
            page_text = spider.driver.page_source
            # 篡改响应对象
            return HtmlResponse(url=spider.driver.current_url,body=page_text,encoding='utf-8',request=request)
        elif 'https://list.jd.com/list.html?cat=' in request.url:
            spider.driver.get(url=request.url)
            js = 'window.scrollTo(0,{})'.format(8000)  # js语句
            spider.driver.execute_script(js)           # 执行js的方法
            time.sleep(2)
            page_text = spider.driver.page_source
            return HtmlResponse(url=spider.driver.current_url,body=page_text,encoding='utf-8',request=request)
        else:
            return response

⑤配置开启下载中间件:

# settings.py文件里配置:
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
   'jd.middlewares.JdDownloaderMiddleware': 543,
}

⑥拓展:通过编写中间件实现每次请求使用随机UA:

1.编写随机UA中间件:

# 在middlewares.py文件中加入以下中间件类:
#UA池
from .settings import user_agent_list
import random
class User_AgentDownloaderMiddleware(object):
    def process_request(self, request, spider):
        #在Request中添加请求头
        request.headers["User-Agent"]=random.choice(user_agent_list) #随机选择一个UA
        return None

2.settings.py文件中俩配置:

# 1.settings.py文件中对应位置开启随机UA中间件;
# 2.settings.py文件中加入准备好的UA:
user_agent_list=[
    "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.29 Safari/525.13",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/531.4 (KHTML, like Gecko) Chrome/3.0.194.0 Safari/531.4",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.11 Safari/534.16",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.50 Safari/525.19",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.7 Safari/532.0",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; Lunascape 5.0 alpha2)",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.7 Safari/532.2",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; ru-RU) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.11 Safari/534.16",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.10 Safari/532.0",
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Maxthon;",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.169.0 Safari/530.1",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7) Gecko/20040614 Firefox/0.9",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.810.0 Safari/535.1",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/4.0.211.0 Safari/532.0",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.3.4000 Chrome/30.0.1599.101 Safari/537.36",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.6 (KHTML, like Gecko) Chrome/7.0.500.0 Safari/534.6",
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; TencentTraveler)",
    "Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
    "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.4 (KHTML, like Gecko) Chrome/6.0.481.0 Safari/534.4",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.370.0 Safari/533.4",
    "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0",
    "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.4.154.31 Safari/525.19",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.1.17) Gecko/20110123 (like Firefox/3.x) SeaMonkey/2.0.12",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.428.0 Safari/534.1",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/7.0.540.0 Safari/534.10",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; de-DE) Chrome/4.0.223.3 Safari/532.2",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/12.0.702.0 Safari/534.24",
    "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/1.0.154.42 Safari/525.19",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.3 (KHTML, like Gecko) Chrome/4.0.227.0 Safari/532.3",
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.8",
    "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.460.0 Safari/534.3",
    "Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.463.0 Safari/534.3",
    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/528.9 (KHTML, like Gecko) Chrome/2.0.157.0 Safari/528.9",
    "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.794.0 Safari/535.1",
    "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.694.0 Safari/534.24",
    "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
    "Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50",
    "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120427 Firefox/15.0a1",
    "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
]

⑦拓展:通过编写中间件实现IP池中使用随机IP:

本人使用的是webapi,直接连接至快代理中的隧道代理!操作简单。只需在中间件中加入以下代码,并在快代理中将你本机外网IP加入白名单即可直接使用!

# 在middlewares.py文件中加入以下中间件即可:
#IP池
from scrapy import signals
from w3lib.http import basic_auth_header
class ProxyDownloaderMiddleware:
    def process_request(self, request, spider):
        proxy = "tps191.kdlapi.com:15818"
        request.meta['proxy'] = "http://%(proxy)s" % {'proxy': proxy}
        # 用户名密码认证
        # request.headers['Proxy-Authorization'] = basic_auth_header('${username}', '${password}')  # 白名单认证可注释此行
        return None

注意:如果出现Bug,或者没出你也为了预防,在settings.py中进行如下配置:
在这里插入图片描述

⑧settings.py中配置:

1.关闭君子协议:

在这里插入图片描述

2.开启下载延迟:(必开!)
在这里插入图片描述3.配置日志:(将日志信息写入文件中,而不再在控制台输出!)
在这里插入图片描述

⑧管道保存数据:(编写items.py文件和pipelines.py文件)

第一部分:编写items.py文件(结构化字段)

# -*- coding: utf-8 -*-

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy


class JdItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    big_category = scrapy.Field()
    big_category_link = scrapy.Field()

    small_category = scrapy.Field()
    small_category_link = scrapy.Field()

    book_name = scrapy.Field()
    book_link = scrapy.Field()
    book_price = scrapy.Field()
    book_pic_link = scrapy.Field()

第二部分:编写pipelines.py文件(进行数据持久化的处理)

第一种:本地文件保存!

1.在pipelines.py文件中编写如下:

# -*- coding: utf-8 -*-

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import json
import pymysql


class JdPipeline(object):
    def __init__(self):
        self.file = open('book.json', 'w', encoding='utf-8')

    def process_item(self, item, spider):
        # 将item对象强制转为字典,该操作只能在scrapy中使用
        item = dict(item)
        # 爬虫文件中提取数据的方法每yield一次,就会运行一次
        # 该方法为固定名称函数
        # 默认使用完管道,需要将数据返回给引擎
        # 1.将字典数据序列化
        '''ensure_ascii=False 将unicode类型转化为str类型,默认为True'''
        json_data = json.dumps(item, ensure_ascii=False, indent=2) + ',\n'

        # 2.将数据写入文件
        self.file.write(json_data)

        return item

    def __del__(self):
        self.file.close()

2.settings.py配置启用管道:

在这里插入图片描述

第二种:本地MySql数据库保存!(注意:使用之前一定要创建对应的数据库和数据表!)

1.在pipelines.py文件中加入如下代码:

class JDSqlPipeline(object):
    # 1.连接数据库
    def open_spider(self,spider):
        data_config=spider.settings["DATABASE_CONFIG"]
        if data_config["type"]=="mysql":
            self.conn=pymysql.connect(**data_config["config"])
            self.cursor=self.conn.cursor()

    def process_item(self,item,spider):
        dict(item)
        sql='insert into JD_book (big_category, big_category_link, small_category, small_category_link, book_name, book_link, book_price, book_pic_link) values(%s,%s,%s,%s,%s,%s,%s,%s)'
        self.cursor.execute(sql,
                                (
                                    item["big_category"],
                                    item["big_category_link"],
                                    item["small_category"],
                                    item["small_category_link"],
                                    item["book_name"],
                                    item["book_link"],
                                    item["book_price"],
                                    item["book_pic_link"]
                                )
                            )
        self.conn.commit()
        return item
    def close_spider(self,spider):
        self.cursor.close()
        self.conn.close()

2.settings.py配置启用管道:

在这里插入图片描述
3.settings.py配置连接本地MySql的信息:
在这里插入图片描述

运行很完美!!!(本地和MySql数据库中数据都正常。)

在这里插入图片描述

标签:5.0,Windows,Gecko,Mozilla,selenium,爬取,item,Scrapy,NT
来源: https://blog.51cto.com/u_15264787/2886763

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有