SCrapy爬蟲大戰京東商城
引言
上一篇已經講過怎樣獲取鏈接,怎樣獲得參數了,詳情請看python爬取京東商城普通篇
代碼詳解
- 首先應該構造請求,這里使用scrapy.Request,這個方法默認調用的是
start_urls
構造請求,如果要改變默認的請求,那么必須重載該方法,這個方法的返回值必須是一個可迭代的對象,一般是用yield
返回,代碼如下:
def start_requests(self):
for i in range(1,101):
page=i*2-1 #這里是構造請求url的page,表示奇數
url=self.start_url+str(page)
yield scrapy.Request(url,meta={'search_page':page+1},callback=self.parse_url) #這里使用meta想回調函數傳入數據,回調函數使用response.meta['search-page']接受數據
下面就是解析網頁了,從上面看出這里的解析回調函數是
parse_url
,因此在此函數中解析網頁。這里還是和上面說的一樣,這個url
得到的僅僅是前一半的信息,如果想要得到后一半的信息還有再次請求,這里還有注意的就是一個技巧:一般先解析出一個數據的數組,不急著取出第一個數,先要用if語句判斷,因為如果得到的是[]
,那么直接取出[0]
是會報錯的,這只是一個避免報錯的方法吧,代碼如下:
def parse_url(self,response):
if response.status==200: #判斷是否請求成功
# print response.url
pids = set() #這個集合用于過濾和保存得到的id,用于作為后面的ajax請求的url構成
try:
all_goods = response.xpath("http://div[@id='J_goodsList']/ul/li") #首先得到所有衣服的整個框架,然后從中抽取每一個框架
for goods in all_goods: #從中解析每一個
# scrapy.shell.inspect_response(response,self) #這是一個調試的方法,這里會直接打開調試模式
items = JdSpiderItem() #定義要抓取的數據
img_url_src = goods.xpath("div/div[1]/a/img/@src").extract() # 如果不存在就是一個空數組[],因此不能在這里取[0]
img_url_delay = goods.xpath(
"div/div[1]/a/img/@data-lazy-img").extract() # 這個是沒有加載出來的圖片,這里不能寫上數組取第一個[0]
price = goods.xpath("div/div[3]/strong/i/text()").extract() #價格
cloths_name = goods.xpath("div/div[4]/a/em/text()").extract()
shop_id = goods.xpath("div/div[7]/@ data-shopid").extract()
cloths_url = goods.xpath("div/div[1]/a/@href").extract()
person_number = goods.xpath("div/div[5]/strong/a/text()").extract()
pid = goods.xpath("@data-pid").extract()
# product_id=goods.xpath("@data-sku").extract()
if pid:
pids.add(pid[0])
if img_url_src: # 如果img_url_src存在
print img_url_src[0]
items['img_url'] = img_url_src[0]
if img_url_delay: # 如果到了沒有加載完成的圖片,就取這個url
print img_url_delay[0]
items['img_url'] = img_url_delay[0] # 這里如果數組不是空的,就能寫了
if price:
items['price'] = price[0]
if cloths_name:
items['cloths_name'] = cloths_name[0]
if shop_id:
items['shop_id'] = shop_id[0]
shop_url = "https://mall.jd.com/index-" + str(shop_id[0]) + ".html"
items['shop_url'] = shop_url
if cloths_url:
items['cloths_url'] = cloths_url[0]
if person_number:
items['person_number'] = person_number[0]
# if product_id:
# print "************************************csdjkvjfskvnk***********************"
# print self.comments_url.format(str(product_id[0]),str(self.count))
# yield scrapy.Request(url=self.comments_url.format(str(product_id[0]),str(self.count)),callback=self.comments)
#yield scrapy.Request寫在這里就是每解析一個鍵褲子就會調用回調函數一次
yield items
except Exception:
print "********************************************ERROR**********************************************************************"
yield scrapy.Request(url=self.search_url.format(str(response.meta['search_page']),",".join(pids)),callback=self.next_half_parse) #再次請求,這里是請求ajax加載的數據,必須放在這里,因為只有等到得到所有的pid才能構成這個請求,回調函數用于下面的解析
- 從上面代碼的最后可以看出最后就是解析
ajax
加載的網頁了,這里調用的next_half_parse
函數,和解析前面一個網頁一樣,這里需要的注意的是,如果前面定義的數據沒有搜索完畢是不能使用yield items
的,必須將items通過meta傳入下一個回調函數繼續完善后才能yield items
,這里就不需要了,代碼如下:
#分析異步加載的網頁
def next_half_parse(self,response):
if response.status==200:
print response.url
items=JdSpiderItem()
#scrapy.shell.inspect_response(response,self) #y用來調試的
try:
lis=response.xpath("http://li[@class='gl-item']")
for li in lis:
cloths_url=li.xpath("div/div[1]/a/@href").extract()
img_url_1=li.xpath("div/div[1]/a/img/@src").extract()
img_url_2=li.xpath("div/div[1]/a/img/@data-lazy-img").extract()
cloths_name=li.xpath("div/div[4]/a/em/text()").extract()
price=li.xpath("div/div[3]/strong/i/text()").extract()
shop_id=li.xpath("div/div[7]/@data-shopid").extract()
person_number=li.xpath("div/div[5]/strong/a/text()").extract()
if cloths_url:
print cloths_url[0]
items['cloths_url']=cloths_url[0]
if img_url_1:
print img_url_1[0]
items['img_url']=img_url_1
if img_url_2:
print img_url_2[0]
items['img_url']=img_url_2[0]
if cloths_name:
items['cloths_name']=cloths_name[0]
if price:
items['price']=price[0]
if shop_id:
items['shop_id']=shop_id[0]
items['shop_url']="https://mall.jd.com/index-" + str(shop_id[0]) + ".html"
if person_number:
items['person_number']=person_number[0]
yield items #又一次的生成,這里是完整的數據,因此可以yield items
except Exception:
print "**************************************************"
- 當然這里還用到了設置請求池,
mysql
存儲,沒有使用到ip
代理,這個在我前面的博客中又講到,這里就不再贅述了,想看源代碼的朋友請點擊這里
小技巧
- 人們會抱怨為什么自己的爬蟲在中途斷開就要重頭開始爬,為什么不能從斷開那里開始爬呢,這里提供一個方法:在配置文件
settings.py
中加入JOBDIR=file_name
,這里的file_name
是一個文件的名字
- 設置下載延遲防止被
ban
:DOWNLOAD_DELAY = 2
:設置每一次的間隔時間RANDOMIZE_DOWNLOAD_DELAY = True
:這個是隨機設置延遲時間 在設置的時間的0.5-1.5
倍之間,這樣可以更有效的防止被ban,一般是配套使用的
ROBOTSTXT_OBEY = False
:這里是表示不遵循robots.txt
文件,默認是True
表示遵循,這里將之改成False
CONCURRENT_REQUESTS
:設置最大請求數,這里默認的時16
,我們可以根據自己電腦的配置改的大一點來加快請求的速度