案例:爬取58同城
爬取58同城步驟如下:
- 在命令行輸入 scrapy startproject city58,使用cd city58進入項目目錄,使用scrapy genspider city58_test 58.com生成爬蟲
訪問58同城網站,打開開發者工具,查看elements
查找含有目標信息的元素,提取目標網頁的url,填入新建的Scrapy項目中Spider文件下的start_url中,並編寫Spider
編寫Items(用於定義需要爬取的字段)和PIpeLine(處理Item)兩個文件,以及修改setting文件(啓動管道),創建main文件
運行main文件,啓動Scrapy爬蟲
文件結構如下圖:
代碼如下:
items.py:定義我們所要爬取的信息的相關屬性,此例中需要爬取的是name、price、url
import scrapy
class City58Item(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field()
price = scrapy.Field()
url = scrapy.Field()
city58_test.py:主要是用於定義請求鏈接,並使用pyquery選取目標元素
import scrapy
from pyquery import PyQuery
from ..items import City58Item
#點點一個是當前目錄一個是上級目錄
class City58TestSpider(scrapy.Spider):
name = 'city58_test' #必不可少的爬蟲名字,啓動的關鍵
allowed_domains = ['58.com'] #主域名
start_urls = ['http://bj.58.com/chuzu/'] #開始爬取的鏈接
def parse(self, response):
jpy = PyQuery(response.text)#生成選擇器對象
li_list = jpy('body > div.mainbox > div.main > div.content > div.listBox > ul > li').items() #記得帶上.items()參數
for it in li_list:
a_tag = it(' div.des > h2 > a')
item = City58Item()
item['name'] = a_tag.text() #a_tag取出文本
item['url'] = a_tag.attr('href') #取出href參數
item['price'] = it('div.listliright > div.money > b').text()
yield item #把Item返回給引擎
##路徑選擇,可以直接右鍵元素-copy-copy select即可快速修改包裝路徑
pipeline.py:當item數據被city58_test爬蟲爬取好並返回給引擎以後,引擎會把item交給City58Pipeline這個管道處理。這個pipeline文件負責打開關閉文件,並寫入文件
import json
class City58Pipeline(object): #這個名字要和setting文件中的後半部分相同
#打開文件
def open_spider(self,spider):
self.file = open('58_chuzu.txt', 'w' , encoding='utf8') #創建文件名,w寫入的方式
print('打開文件了')
#寫入文件
def process_item(self, item, spider):
line = '{}\n'.format(json.dumps(dict(item))) #把item轉換成字符串,並且換行寫入不然是一坨了,並且注意是dict不是dic別寫錯
#注意此處的寫法,不然最後生成的文件出錯
self.file.write(line)
return item #必須返回item
#關閉文件
def close_spider(self, spider):
self.file.close()
print('關閉文件了')
###### settings.py:開啓Ci
###以上問題總結:如何寫入文件:
如何把一個個對象寫入文件。涉及到序列化,即把對象變成一個文本。如何把dictionary寫入到文本中呢?經典方案是import jaon,把它變成一個json的字符串,然後存起來就ok了。
即:序列號函數json.dumps(),item其實就是一個dictionaty,但是離真正還是差一點。
於是json.dumps(dic(item)讓其變成真正的dic,並把其放入line變量中,即line=json.dumps(dic(item)。
--------------------------------------------------------------------------------
這其中有個問題,容易寫成一坨,就是你不斷地這麼寫只是在尾部追加,沒有換行符,
這時候我們要添加一個換行的機制,並把其format粘貼進來
ty58Pipeline這個管道,激活管道,setting文件
#取消註釋即可
ITEM_PIPELINES = {
‘city58.pipelines.City58Pipeline’: 300,
}
main.py:運行爬蟲,創建main.py文件
並且把執行命令寫進去,其中有個是文件名,然後debug運行即可。
from scrapy import cmdline
cmdline.execute("scrapy crawl city58_test".split())
若存在mongodb中 需要更改setting 文件和pipeline文件,其他文件保持不變,如下所示:
setting.py
ITEM_PIPELINES = {
'city_58.pipelines.City58Pipeline': 300,
}
MONGO_HOST = '127.0.0.1' # 主機IP
MONGO_PORT = 27017 #端口名
MONGO_DB = '58' #數據庫名
MONGO_COLL= 'hezu' #表名
pipline.py
import json
import pymongo
from scrapy.conf import settings
class City58Pipeline(object): #這個名字要和setting文件中的後半部分相同
def __init__(self):
#鏈接數據庫
self.client = pymongo.MongoClient(host=settings['MONGO_HOST'], port=settings['MONGO_PORT'])
# 數據庫登錄需要帳號密碼的話
# self.client.admin.authenticate(settings['MINGO_USER'], settings['MONGO_PSW'])
self.db = self.client[settings['MONGO_DB']] # 獲得數據庫的句柄
self.coll = self.db[settings['MONGO_COLL']] # 獲得collection的句柄
def process_item(self, item, spider):
line = dict(item) #注意此處的寫法
self.coll.insert(line) # 向數據庫插入一條記錄
return item
課後作業
試試看,通過自己查資料,能否搞明白以下代碼的作用:
import scrapy
import hashlib
from urllib.parse import quote
class ScreenshotPipeline(object):
"""Pipeline that uses Splash to render screenshot of
every Scrapy item."""
SPLASH_URL = "http://localhost:8050/render.png?url={}"
def process_item(self, item, spider):
encoded_item_url = quote(item["url"])
screenshot_url = self.SPLASH_URL.format(encoded_item_url)
request = scrapy.Request(screenshot_url)
dfd = spider.crawler.engine.download(request, spider)
dfd.addBoth(self.return_item, item)
return dfd
def return_item(self, response, item):
if response.status != 200:
# Error happened, return item.
return item
# Save screenshot to file, filename will be hash of url.
url = item["url"]
url_hash = hashlib.md5(url.encode("utf8")).hexdigest()
filename = "{}.png".format(url_hash)
with open(filename, "wb") as f:
f.write(response.body)
# Store filename in item.
item["screenshot_filename"] = filename
return item
補充資料
有能力的同學可以去GitHub瀏覽Scrapy的源碼,更深入地理解Scrapy的使用和操作方法
有興趣的同學可以嘗試去爬取京東的商品,這裏有一個京東爬蟲開源的項目,大家可以參考一下