爬取思路架構:
- 先建立一個scrapy項目
- 編寫items檔案
- 建立爬蟲
- 修改middlewares
- 修改pipelines
- 配置settings
- 運作Scrapy
直接進入正題:
1、先建立一個scrapy項目
在系統指令行輸入:
scrapy startproject jd
- 項目建立成功後,會出現下圖所示檔案。 各個主要檔案的作用:
scrapy.cfg :項目的配置檔案
jd/ :項目的Python子產品,将會從這裡引用代碼
jd/items.py :項目的目标檔案
jd/pipelines.py :項目的管道檔案
jd/settings.py :項目的設定檔案
jd/spiders/ :存儲爬蟲代碼目錄
2、編寫items檔案
我們這裡主要爬取商品清單裡的商品名稱、價格、店鋪、評論條數、商品詳情的url和商品的提供商是否為自營,代碼如下:
import scrapy
class JdItem(scrapy.Item):
# define the fields for your item here like:
#名字
name = scrapy.Field()
#價格
price = scrapy.Field()
#店鋪
store = scrapy.Field()
#評論條數
evaluate_num = scrapy.Field()
#商品url
detail_url = scrapy.Field()
#提供商
support = scrapy.Field()
3、items檔案寫完之後,就要制作我們的爬蟲啦,在系統指令行中目前目錄下輸入指令,将在jd/spider目錄下建立一個名為jingdong的爬蟲,并指定爬取域的範圍,代碼如下:
- 打開 jd/spider目錄裡的 jingdong.py,要建立一個Spider, 你必須用scrapy.Spider類建立一個子類,并确定了三個強制的屬性 和 一個方法。
name = “” :這個爬蟲的識别名稱,必須是唯一的,在不同的爬蟲必須定義不同的名字。
allow_domains = [] 是搜尋的域名範圍,也就是爬蟲的限制區域,規定爬蟲隻爬取這個域名下的網頁,不存在的URL會被忽略。
start_urls = () :爬取的URL元祖/清單。爬蟲從這裡開始抓取資料,是以,第一次下載下傳的資料将會從這些urls開始。其他子URL将會從這些起始URL中繼承性生成。
parse(self, response) :解析的方法,每個初始URL完成下載下傳後将被調用,調用的時候傳入從每一個URL傳回的Response對象來作為唯一參數,主要作用如下:
負責解析傳回的網頁資料(response.body),提取結構化資料(生成item)
生成需要下一頁的URL請求。
- 了解了各自的使用方法後,修改代碼為以下:
# -*- coding: utf-8 -*-
import scrapy
from jd.items import JdItem
class JingdongSpider(scrapy.Spider):
name = "jingdong"
allowed_domains = ["search.jd.com"]
#這裡我爬取的是手機,可根據要爬取的不同商品,修改關鍵詞
base_url = 'https://search.jd.com/Search?keyword=%E6%89%8B%E6%9C%BA&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq=%E6%89%8B%E6%9C%BA&cid2=653&cid3=655&psort=4&click=0'
page =
start_urls = [base_url + '&page=' + str(page) + '&click=0']
def start_requests(self):
yield scrapy.Request(url = self.base_url,callback=self.parse,meta={'page':self.page},dont_filter=True)
def parse(self,response):
#商品清單,在網頁源碼上用xpath盡情解析吧,每個item都加上try,except使程式更加強壯
products = response.xpath('//ul[@class="gl-warp clearfix"]/li')
#清單疊代
for product in products:
item = JdItem()
try:
name = ''.join(product.xpath('.//div[@class="p-name p-name-type-2"]/a/em/text()').extract()).strip().replace(' ','')
except:
name = ''
try:
price = product.xpath('.//div[@class="p-price"]//i/text()').extract()[]
except:
price = ''
try:
store = product.xpath('.//div[@class="p-shop"]//a/@title').extract()[]
except:
store = ''
try:
evaluate_num = product.xpath('.//div[@class="p-commit"]/strong/a/text()').extract()[]
except:
evaluate_num = ''
try:
detail_url = product.xpath('.//div[@class="p-name p-name-type-2"]/a/@href').extract()[]
except:
detail_url = ''
try:
if product.xpath('.//div[@class="p-icons"]/i/text()').extract()[]=='自營':
support = '自營'
else:
support = '非自營'
except:
support = '非自營'
item['name'] = name
item['price'] = price
item['store'] = store
item['evaluate_num'] = evaluate_num
item['detail_url'] = detail_url
item['support'] = support
#這裡的yield将資料交給pipelines
yield item
print(item)
#這裡的目的是配合middlewares中的slenium配合,這裡每次都要打開相同的網頁self.base_url,然後運用selenium操作浏覽器,在最下方的頁碼中輸入要查詢的頁數,我們這裡查詢1-100頁
if self.page < :
self.page +=
print(self.page)
#這裡的meta使用來傳遞page參數,dont_filter表示不去重,因為scrapy預設會去重url,我們每次請求的網頁都是重複的,是以這裡不去重
yield scrapy.Request(url=self.base_url,callback=self.parse,meta={'page':self.page},dont_filter=True)
4、修改middlewares,把請求傳送給middlewares的selenium,由selenium發送請求,并将Response傳給jingdong.py的parse函數解析。代碼如下:
# -*- coding: utf-8 -*-
# Define here the models for your spider middleware
#
# See documentation in:
# http://doc.scrapy.org/en/latest/topics/spider-middleware.html
from scrapy import signals
#導入 webdriver
from selenium import webdriver
from selenium.webdriver.common.by import By
#WebDriverWait庫,負責循環等待
from selenium.webdriver.support.ui import WebDriverWait
#excepted_conditions類,負責條件出發
from selenium.webdriver.support import expected_conditions as EC
#
from scrapy.http import HtmlResponse
from scrapy.conf import settings
import random
import time
#随機使用user_agent,user_agents從settings中讀取
class JdDownloadmiddlewareRandomUseragent(object):
def __init__(self):
self.useragents = settings['USER_AGENTS']
def process_request(self,request,spider):
useragent = random.choice(self.useragents)
print(useragent)
request.headers.setdefault('User-Agent',useragent)
#下載下傳中間件,用selenium爬取,這裡我用的是Firefox的Webdriver,另外還帶了Chromdriver的使用代碼
class JdSpiderMiddleware(object):
# Not all methods need to be defined. If a method is not defined,
# scrapy acts as if the spider middleware does not modify the
# passed objects.
def __init__(self):
print('打開了火狐浏覽器')
#firefox浏覽器
firefox_profile = webdriver.FirefoxProfile()
fireFoxOptions = webdriver.FirefoxOptions()
#設定無圖打開
firefox_profile.set_preference('permissions.default.image', )
#設定不加載Flash
firefox_profile.set_preference('dom.ipc.plugins.enabled.libflashplayer.so', 'False')
#設定悟透浏覽器
fireFoxOptions.set_headless()
fireFoxOptions.add_argument(')
#timeout表示網頁請求逾時
self.browser = webdriver.Firefox(firefox_options=fireFoxOptions,firefox_profile=firefox_profile,timeout=)
self.wait = WebDriverWait(self.browser,timeout=)
'''
#Chrome浏覽器
options = webdriver.ChromeOptions()
#設定中文
#options.add_argument(')
#設定無圖加載 1 允許所有圖檔; 2 阻止所有圖檔; 3 阻止第三方伺服器圖檔
prefs = {
'profile.default_content_setting_values':{
'images': 2
}
}
options.add_experimental_option('prefs',prefs)
#設定無頭浏覽器
options.add_argument('--headless')
self.browser = webdriver.Chrome(chrome_options=options)
#設定等待請求網頁時間最大為self.timeout
self.wait = WebDriverWait(self.browser,self.timeout)
self.browser.set_page_load_time(self.timeout)
'''
def __del__(self):
print('關閉Firefox')
#爬蟲結束後,關閉浏覽器
self.browser.close()
def process_request(self,request,spider):
page = request.meta.get('page',)
try:
print('Selenium啟動解析')
self.browser.get(request.url)
#滾動條下拉到底
self.browser.execute_script("document.documentElement.scrollTop=10000")
#等待網頁加載完畢
time.sleep()
#如果傳過來的page不是第一頁就需要在最下面的輸入頁碼處,輸入page,并按确定鍵跳轉到指定頁面
if page > :
input = self.wait.until(EC.presence_of_element_located((By.XPATH,'.//span[@class="p-skip"]/input'))) # 擷取輸入頁面數框
submit = self.wait.until(EC.element_to_be_clickable((By.XPATH,'.//span[@class="p-skip"]/a'))) # 擷取确定按鈕
input.clear()
input.send_keys(page)
submit.click()
#滾動條下拉到底,第二種寫法
self.browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep()
# 如果當 str(page),即目前頁碼出現在高亮文本的時候,就代表頁面成功跳轉
self.wait.until(
EC.text_to_be_present_in_element((By.XPATH,'.//span[@class="p-num"]/a[@class="curr"]'),str(page)))
# 等待加載完所有的商品list 然後進一步解析
self.wait.until(EC.element_to_be_clickable((By.XPATH,'.//span[@class="p-skip"]/a')))
#self.wait.until(EC.presence_of_element_located((By.XPATH,'.//ul[@class="gl-warp clearfix"]/li')))
time.sleep()
body = self.browser.page_source
print('selenium開始通路第'+str(page)+'頁')
#将selenium得到的網頁資料傳回給parse解析
return HtmlResponse(url=request.url,body=body,encoding='utf-8',request=request)
except Exception as E:
print(str(E))
return HtmlResponse(url =request.url,status=,request=request)
5、修改pipelines,jingdong.py中parse函數中的yield item的資料給到了pipelines,将資料存入到Mongodb資料庫,代碼如下:
# -*- coding: utf-8 -*-
import pymongo
class JdPipeline(object):
def __init__(self):
self.client = pymongo.MongoClient('localhost',)
scrapy_db = self.client['jd'] # 建立資料庫
self.coll = scrapy_db['scrapyphone'] # 建立資料庫中的表格
def process_item(self, item, spider):
self.coll.insert_one(dict(item))
return item
def close_spider(self, spider):
self.client.close()
6、配置settings,為減少篇幅,隻把修改的地方放了上來,其他的預設就可以
BOT_NAME = 'jd'
SPIDER_MODULES = ['jd.spiders']
NEWSPIDER_MODULE = 'jd.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'jd (+http://www.yourdomain.com)'
#USER_AGENTS清單
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
"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 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
"Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0"
]
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
#下載下傳延時,一般設定2.5左右,防止被封哦
DOWNLOAD_DELAY =
#禁止cookies
# Disable cookies (enabled by default)
COOKIES_ENABLED = False
#設定下載下傳中間件,把我們自己寫的加進去,後面的數字越小,代表執行優先級越高
# Enable or disable spider middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
DOWNLOADER_MIDDLEWARES = {
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware':None,
'jd.middlewares.JdDownloadmiddlewareRandomUseragent':,
'jd.middlewares.JdSpiderMiddleware':
}
# Enable or disable extensions
# See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
#PIPELINES把我們自己寫的存儲類加上
ITEM_PIPELINES = {
'jd.pipelines.JdPipeline': ,
}
7、運作程式
在系統指令行中的jd目錄下,執行代碼:
scrapy crawl jingdong
代碼就跑起來了,大功告成,100頁,每頁60個,共爬取6000條商品:
存入Mongodb資料庫,檢視如下圖:
-
由于京東商品清單頁為ajax請求,正常的請求隻能拿到一半的資料,另一半資料需要下拉滾動條才會顯示,是以我們用selenium模拟浏覽器下拉操作通路網頁,才能得到完整的資料。雖然用的selenium,但是爬取速度也還算可以,不知道您看完之後,是否學會了使用Scrapy + Selenium爬取網頁呢,如果有不懂的地方,可以在下方留言,一起進步!
需要源碼的可去Github下載下傳,歡迎star和提出問題:
https://github.com/wangyeqiang/Craw
最後希望,早日學會Scrapy + Selenium。