关于scrapy以及使用的代理轮换中间件请参考我的爬取豆瓣文章:

【scrapy】scrapy按分类爬取豆瓣电影基础信息

http://blog.csdn.net/qqxx6661/article/details/56017386


爬虫简介

主要还是按照scrapy的设计思路来爬,上一篇文章的豆瓣爬取能够很好的反应这种思路,京东爬虫也是如此。

主要思路是:获取手机分类(自营)页面——扫描该页所有商品ID——进入每个商品页面获取除价格外所有信息——获取商品价格信息——扫描下一页网址——进行下一页商品ID获取.....

京东爬虫特殊性

显然商城类都有严格的反爬虫,所以这篇笔记主要围绕如何解决几个反爬问题来写的。

价格抓取


价格在页面完整载入后审查元素时是可以看见的,不过其实是加载了JS,所以实际上源代码内不包含价格。需要查看JS加载的情况。如下图


在写这篇笔记的时候,我代码里的JS名称似乎已经失效了。不过现在看来基本都是类似这种mgets?的script。

所以直接让价格被单独抓取,代码形如:

 def parse_price(self, response):
        item = response.meta['item']
        price_str = response.body
        price_str = price_str[2:-4]
        js = json.loads(str(price_str))
        print js['p']
        item['phone_price'] = js['p']
        yield item
        # return item

    def parse_each_phone(self, response):
        item = jdItem()
        each_id = response.xpath('//ul[@class="parameter2 p-parameter-list"]/li[2]/@title').extract()
        item['phone_id'] = each_id
        item['phone_name'] = response.xpath('normalize-space(//div[@class="sku-name"]/text())').extract() #到了美国有空格了,不知道为何,已修复
        item['phone_houdu'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[2]/dl/dd[4]/text()').extract()
        item['phone_CPU'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[4]/dl/dd[1]/text()').extract()
        item['phone_ROM'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[6]/dl/dd[1]/text()').extract()
        item['phone_RAM'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[6]/dl/dd[2]/text()').extract()
        item['phone_screen'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[7]/dl/dd[2]/text()').extract()
        item['phone_frontcam'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[8]/dl/dd[1]/text()').extract()
        item['phone_backcam'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[9]/dl/dd[2]/text()').extract()
        each_id = str(each_id[0])
        url = "https://p.3.cn/prices/mgets?callback=&skuIds=J_" + each_id
        yield scrapy.Request(url, meta={'item': item}, callback=self.parse_price)

里面涉及到价格连接的字符串拼接,这个多在shell里面尝试,直到能够正确切出价格。


allowed_domains注意

写代码的时候卡了好久,价格永远抓取不到,各种查资料,最后突然意识到是allowed_domains被限制在了jd.com,而价格其实在3.cn开头的链接里。智障。

class jdSpider(scrapy.Spider):
    name = "jd"
    allowed_domains = ["jd.com",
                       "3.cn"]
    start_urls = [
        "http://list.jd.com/list.html?cat=9987,653,655&page=1&delivery=1&sort=sort_rank_asc&trans=1&JL=4_10_0#J_main"
    ]

jd_spider.py

给出主要代码

# -*- coding: utf-8 -*-
import scrapy
from scrapy_yzd.items import jdItem
import json
import time


class jdSpider(scrapy.Spider):
    name = "jd"
    allowed_domains = ["jd.com",
                       "3.cn"]
    start_urls = [
        "http://list.jd.com/list.html?cat=9987,653,655&page=1&delivery=1&sort=sort_rank_asc&trans=1&JL=4_10_0#J_main"
    ]

    def parse(self, response):
        a = 0
        for href in response.xpath('//div/@data-sku'):
            url = "https://item.jd.com/" + href.extract() + ".html"
            print url
            yield scrapy.Request(url, callback=self.parse_each_phone)
            a += 1
        print "This page's phone number:" + str(a)

        time.sleep(20)
        next_page = response.xpath('//a[@class="pn-next"]/@href').extract()
        if next_page:
            next_page = "https://list.jd.com" + next_page[0]
            print '--------------Finding next page--------------------------', next_page
            yield scrapy.Request(next_page, callback=self.parse)
        else:
            print '--------------There is no more page!--------------------------'

    def parse_price(self, response):
        item = response.meta['item']
        price_str = response.body
        price_str = price_str[2:-4]
        js = json.loads(str(price_str))
        print js['p']
        item['phone_price'] = js['p']
        yield item
        # return item

    def parse_each_phone(self, response):
        item = jdItem()
        each_id = response.xpath('//ul[@class="parameter2 p-parameter-list"]/li[2]/@title').extract()
        item['phone_id'] = each_id
        item['phone_name'] = response.xpath('normalize-space(//div[@class="sku-name"]/text())').extract() #到了美国有空格了,不知道为何,已修复
        item['phone_houdu'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[2]/dl/dd[4]/text()').extract()
        item['phone_CPU'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[4]/dl/dd[1]/text()').extract()
        item['phone_ROM'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[6]/dl/dd[1]/text()').extract()
        item['phone_RAM'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[6]/dl/dd[2]/text()').extract()
        item['phone_screen'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[7]/dl/dd[2]/text()').extract()
        item['phone_frontcam'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[8]/dl/dd[1]/text()').extract()
        item['phone_backcam'] = response.xpath('//*[@id="detail"]/div[2]/div[2]/div[1]/div[9]/dl/dd[2]/text()').extract()
        each_id = str(each_id[0])
        url = "https://p.3.cn/prices/mgets?callback=&skuIds=J_" + each_id
        yield scrapy.Request(url, meta={'item': item}, callback=self.parse_price)
        # yield item # 导致重复商品且无价格,原因未知

实验结果


未解决的问题

问题很严重,京东似乎对爬虫十分敏感,在连续进行下一页抓取后,直接会回到手机分类的第一页






I love 周雨楠