关于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 周雨楠