Scrapy实战篇(八)之Scrapy对接selenium爬取京东商城商品数据

摘要:
步骤1:设置我们需要提取的字段,即设置Item。py文件。

 本篇目标:我们以爬取京东商城商品数据为例,展示Scrapy框架对接selenium爬取京东商城商品数据。

背景:

  京东商城页面为js动态加载页面,直接使用request请求,无法得到我们想要的商品数据,故需要借助于selenium模拟人的行为发起请求,输出源代码,然后解析源代码,得到我们想要的数据。

第一步:设置我们需要提取的字段,也就是在Scrapy框架中设置Item.py文件。

class ProductItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    dp = Field()
    title = Field()
    price = Field()
    comment=Field()
    url=Field()
    type=Field()

这里我们需提取上面几个字段,依次为店铺名称,商品名称,商品价格,评论数,商品url,类型(区分是什么类型的商品)


第二步:
  设置我们需要从哪个页面开始爬起,也就是开发scrapy框架中的spider文件,代码如下
class JingdongSpider(scrapy.Spider):
    name = 'jingdong'
    allowed_domains = ['www.jingdong.com']
    base_url = 'https://search.jd.com/Search?keyword='

    def start_requests(self):
        for keyword in self.settings.get('KEYWORDS'):
            for page in range(1,self.settings.get('MAX_PAGE') + 1):
                url = self.base_url + quote(keyword)
                #dont_filter = True  不去重
                yield Request(url = url ,callback = self.parse,meta = {'page':page},dont_filter=True)
  我们设置初始url为京东商城搜索商品的页面链接,其中搜索的商品用KEYWORDS表示,在settings文件中以列表的形式设置,因为搜索出来的页数可能很多,所有我们需要爬取的页数页用MAX_PAGE变量
在settings文件中设置。
KEYWORDS=['iPad']
MAX_PAGE=2


如果此时运行项目,链接会直接发送给下载器进行下载,无法得到我们想要的数据,所以我们在下载器中间件中对该请求进行处理。

第三步:
  在下载器中间件中对接selenium,直接输出源代码并返回,不在下载器中下载页面。
class SeleniumMiddleware(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.

    def __init__(self,timeout=None):
        self.logger=getLogger(__name__)
        self.timeout = timeout
        self.browser = webdriver.Chrome()
        self.browser.set_window_size(1400,700)
        self.browser.set_page_load_timeout(self.timeout)
        self.wait = WebDriverWait(self.browser,self.timeout)

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

    @classmethod
    def from_crawler(cls, crawler):
        # This method is used by Scrapy to create your spiders.
        return cls(timeout=crawler.settings.get('SELENIUM_TIMEOUT'))

    def process_request(self, request, spider):
        '''
        在下载器中间件中对接使用selenium,输出源代码之后,构造htmlresponse对象,直接返回
        给spider解析页面,提取数据
        并且也不在执行下载器下载页面动作
        htmlresponse对象的文档:
        :param request:
        :param spider:
        :return:
        '''

        print('Chorme is Starting')
        page = request.meta.get('page', 1)
        self.wait = WebDriverWait(self.browser, self.timeout)
        try:
            self.browser.get(request.url)
            if page > 1:
                input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > input')))
                input.clear()
                input.send_keys(page)
                time.sleep(5)

                # 将网页中输入跳转页的输入框赋值给input变量 EC.presence_of_element_located,判断输入框已经被加载出来
                input = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > input')))
                # 将网页中调准页面的确定按钮赋值给submit变量,EC.element_to_be_clickable 判断此按钮是可点击的
                submit = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#J_bottomPage > span.p-skip > a')))
                input.clear()
                input.send_keys(page)
                submit.click()  # 点击按钮
                time.sleep(5)

                # 判断当前页码出现在了输入的页面中,EC.text_to_be_present_in_element 判断元素在指定字符串中出现
                self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.curr'),str(page)))
                # 等待 #J_goodsList 加载出来,为页面数据,加载出来之后,在返回网页源代码
                self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#J_bottomPage > span.p-num > a.curr'),str(page)))
            return HtmlResponse(url=request.url, body=self.browser.page_source, request=request, encoding='utf-8',status=200)
        except TimeoutException:
            return HtmlResponse(url=request.url, status=500, request=request)

__init__和类函数都执行一些初始化操作,无需多说,我们主要看process_request()方法
首先我们这是浏览器的等待时长,然后我们见输入页码的输入框赋值给input变量,在将翻页的点击按钮框赋值给submit变量,然后在数据框中输入页码,等待页面加载,直接返回
htmlresponse给spider解析,这里我们没有经过下载器下载,直接构造response的子类htmlresponse返回。(当下载器中间件返回response对象时,更低优先级的process_request将不在执行,转而
执行其他的process_response()方法,本例中没有其他的process_response(),所以直接将结果返回给spider解析。)

第四步:
  开发第二步中Request对象中的回调函数,解析页面数据,提取我们想要的数据。这里我们采用BeautifulSoup进行解析,代码如下:
def parse(self, response):
    soup = BeautifulSoup(response.text, 'lxml')
    lis = soup.find_all(name='li', class_="gl-item")
    for li in lis:
        proc_dict = {}
        dp = li.find(name='span', class_="J_im_icon")
        if dp:
            proc_dict['dp'] = dp.get_text().strip()
        else:
            continue
        id = li.attrs['data-sku']
        title = li.find(name='div', class_="p-name p-name-type-2")
        proc_dict['title'] = title.get_text().strip()
        price = li.find(name='strong', class_="J_" + id)
        proc_dict['price'] = price.get_text()
        comment = li.find(name='a', id="J_comment_" + id)
        proc_dict['comment'] = comment.get_text() + '条评论'
        url = 'https://item.jd.com/' + id + '.html'
        proc_dict['url'] = url
        proc_dict['type'] = 'JINGDONG'
        yield proc_dict
第五步:
  提取完页面数据之后,数据会发送到item pipeline处进行数据处理,清洗,入库等操作,所以我们此时当然需要定义项目管道了,在此我们将数据存储在mongodb数据库中。
class MongoPipeline(object):

    def __init__(self,mongo_url,mongo_db,collection):
        self.mongo_url = mongo_url
        self.mongo_db = mongo_db
        self.collection = collection

    @classmethod
    def from_crawler(cls,crawler):
        return cls(
            mongo_url=crawler.settings.get('MONGO_URL'),
            mongo_db = crawler.settings.get('MONGO_DB'),
            collection = crawler.settings.get('COLLECTION')
        )

    def open_spider(self,spider):
        self.client = pymongo.MongoClient(self.mongo_url)
        self.db = self.client[self.mongo_db]

    def process_item(self,item, spider):
        # name = item.__class__.collection
        name = self.collection
        self.db[name].insert(dict(item))
        return item

    def close_spider(self,spider):
        self.client.close()
我们使用类方法from_crawler从settings文件中获取mongodb数据库的配置信息,在__init__中进行初始化,在process_item中将数据存储到mongodb中。

第六步
  1、配置settings文件,将项目中使用到的配置项在settings文件中配置,本项目中使用到了KEYWORDS,MAX_PAGE,SELENIUM_TIMEOUT(页面加载超时时间),MONGOURL,MONGODB,COLLECTION;
  2、修改配置项,激活下载器中间件和item pipeline。
DOWNLOADER_MIDDLEWARES = {
   'scrapyseleniumtest.middlewares.SeleniumMiddleware': 543,
}

ITEM_PIPELINES = {
   'scrapyseleniumtest.pipelines.MongoPipeline': 300,
}
至此,项目中所有需要开发的代码和配置项开发完成,运行项目之后,在mongodb中查看数据,应该已经执行成功。

本项目完整代码:

https://gitee.com/liangxinbin/Scrpay/tree/master/scrapyseleniumtest





免责声明:文章转载自《Scrapy实战篇(八)之Scrapy对接selenium爬取京东商城商品数据》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【Qt开发】Linux下Qt开发环境的安装与集成解决css引用图片不显示问题:background-image: url(../image/document.png);下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

python selenium 编码问题

#coding=utf-8 from selenium import webdriver driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 返回百度页面底部备案信息 text = driver.find_element_by_id("cp").text print(tex...

安装scrapy

Scrapy依赖的库比较多,至少需要依赖库有Twisted 14.0,lxml 3.4,pyOpenSSL 0.14。而在不同平台环境又各不相同,所以在安装之前最好确保把一些基本库安装好,尤其是Windows。 #Windows平台    1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https:...

CSS(Sass)模块化

在Vue大行其道的今天,我们也受益匪浅,再次感谢@尤大。那么在用Vue开发的过程中,我们大概率会用到Sass来提高我们前端的开发效率,为前端工程化做了很大贡献,Sass本身具有很多令人兴奋的功能,例如:variable、function、mixin、nested、module等,本文主要来研究module——CSS模块化。 我们在开发中最常用的模块化指令就...

JMeter处理Cookie与Session

cookie 和session 的区别: 1、cookie数据存放在客户的浏览器上,session数据放在服务器上。 2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗   考虑到安全应当使用session。 3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能   考虑到减轻服务器性能...

2020前端面试题常问集锦

以下为常备面试题集锦,面好多家公司大都问的如此(后续更新补);还有一些算法和手写代码后面整理; js陈述类型1、Es6的class和构造函数的区别: class xx { }(1)不存在变量提升(2)方法默认是不可枚举的,class所有方法没有原型对象prototype也没有构造器不能用new来调用; 2、普通函数和箭头函数的区别?(1)this指向不...

前端url下载视频资源

下载url视频有以下几种方案: 1.a标签download,但是这种因为浏览器机制会先打开预览,图片与视频都是这样。在预览页面右键下载。如果想通过a标签直接下载,可以通过设置Nginx配置。    // url为资源地址 // newName为资源的原名称 // a标签的url a标签的download属性并不能修改名称,这时候我们就需要用到nginx的一...