Scrapy是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。可以應用在包括數據挖掘,信息處理或存儲歷史數據等一系列的程序中。其最初是爲了頁面抓取 (更確切來說, 網絡抓取 )所設計的, 也可以應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。Scrapy用途廣泛,可以用於數據挖掘、監測和自動化測試。
(一)scrapy整體架構圖
(二)Scrapy主要組件
- 1、引擎(Scrapy): 用來處理整個系統的數據流處理, 觸發事務(框架核心)。
- 2、調度器(Scheduler): 用來接受引擎發過來的請求, 壓入隊列中, 並在引擎再次請求的時候返回. 可以想像成一個URL(抓取網頁的網址或者說是鏈接)的優先隊列, 由它來決定下一個要抓取的網址是什麼, 同時去除重複的網址。
- 3、下載器(Downloader): 用於下載網頁內容, 並將網頁內容返回給蜘蛛(Scrapy下載器是建立在twisted這個高效的異步模型上的)。
- 4、爬蟲(Spiders): 爬蟲是主要幹活的, 用於從特定的網頁中提取自己需要的信息, 即所謂的實體(Item)。用戶也可以從中提取出鏈接,讓Scrapy繼續抓取下一個頁面。
- 5、項目管道(Pipeline): 負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體、驗證實體的有效性、清除不需要的信息。當頁面被爬蟲解析後,將被髮送到項目管道,並經過幾個特定的次序處理數據。
- 6、下載器中間件(Downloader Middlewares): 位於Scrapy引擎和下載器之間的框架,主要是處理Scrapy引擎與下載器之間的請求及響應。
- 7、爬蟲中間件(Spider Middlewares): 介於Scrapy引擎和爬蟲之間的框架,主要工作是處理蜘蛛的響應輸入和請求輸出。
- 8、調度中間件(Scheduler Middewares): 介於Scrapy引擎和調度之間的中間件,從Scrapy引擎發送到調度的請求和響應。
scrapy startproject yaowang #創建項目
下面來簡單介紹一下各個主要文件的作用:
scrapy.cfg:項目的配置文件
yaowang/:項目的Python模塊,將會從這裏引用代碼
yaowang/items.py:項目的字段定義文件
yaowang/pipelines.py:項目的管道文件
yaowang/settings.py:項目的設置文件
yaowang/spiders/:存儲爬蟲代碼目錄
(三)定義存儲對象(yaowangl/items.py)
Items是裝載我們抓取數據的容器。它們工作像簡單的Python字典,它提供更多的保護,比如對未定義的字段提供填充功能防止出錯.
Itmes.py
(四)製作爬蟲 (yaowang/spiders/)
1、在當前目錄下輸入命令,將在yaowang/spider目錄下創建一個名爲的爬蟲,並指定爬取域的範圍:
scrapy genspider yao 111.com.cn #創建爬蟲
2、打開 tutorial/spider目錄裏的 hsw.py,默認增加了下列代碼:
yao.py
# -*- coding: utf-8 -*-
import scrapy
from lxml import etree
from yaowang.items import YaowangItem
class YaoSpider(scrapy.Spider):
name = 'yao'
allowed_domains = ['111.com.cn']
start_urls = []
for i in range(1,51):
url = f'https://www.111.com.cn/categories/953710-j{i}.html'
start_urls.append(url)
def parse(self, response):
# with open('yaowang.html','w',encoding='utf-8')as fp:
# fp.write(response.body.decode('gbk'))
# 提取HTML頁面
tree = etree.HTML(response.body.decode('gbk'))
# 所有藥品的盒子
li_list = tree.xpath('//ul[@id="itemSearchList"]/li')
# print(len(li_list))
for li in li_list:
# 實例化item
item = YaowangItem()
# 藥名
name = li.xpath('./div[1]/p[@class="titleBox"]/a/text()')
name = [i.strip() for i in name]
name = ''.join(name).strip()
# print(name)
# 圖片鏈接
href = li.xpath('./div[1]/a//img/@src')[0]
# print(href)
# 價格
price = li.xpath('./div[1]/p[@class="price"]//span/text()')[0]
price = price.strip()
# print(price)
# 藥店
store = li.xpath('./div[1]/div[@class="sell_type_div"]/span[2]//text()')[0]
# print(store)
# 詳情url
url = li.xpath('./div[1]/a/@href')[0]
full_url = "https:" + url
# print(full_url)
item['name'] = name
item['href'] = href
item['price'] = price
item['store'] = store
item['url'] = full_url
# 默認過濾相同的url請求
yield scrapy.Request(url=full_url,callback=self.parse_detail,meta={'data':item})
# yield item
# 二級頁面,詳情頁數據提取
def parse_detail(self,response):
# 繼承上一頁頁面的item數據
item = response.meta['data']
# with open('yaowang_detail.html','w',encoding='utf-8')as fp:
# fp.write(response.body.decode('gbk'))
tree = etree.HTML(response.body.decode('gbk'))
# 商品詳情信息
# div = tree.xpath('//div[@class="goods_intro"]/table')
# 商品名稱
shop_name = tree.xpath('//div[@class="goods_intro"]//tr[1]/td/text()')[0]
print("========",shop_name)
# 品牌
brand = tree.xpath('//div[@class="goods_intro"]//tr[2]/td[1]/text()')[0]
# print("========", brand)
# 規格
standard = tree.xpath('//div[@class="goods_intro"]//tr[2]/td[2]/text()')[0]
# print("========", standard)
# 重量
weight = tree.xpath('//div[@class="goods_intro"]//tr[3]/td[1]/text()')[0]
# print("========", weight)
# 生產廠商
produce = tree.xpath('//div[@class="goods_intro"]//tr[3]/td[2]/text()')[0]
# print("========", produce)
# 批准文號
approval_num = tree.xpath('//div[@class="goods_intro"]//tr[4]/td[1]//text()')
approval_num = [i.strip() for i in approval_num]
approval_num = ''.join(approval_num)
# print("========", approval_num)
# 產品類型
produce_type = tree.xpath('//div[@class="goods_intro"]//tr[4]/td[2]/text()')[0]
produce_type = produce_type.strip()
# print("========", produce_type)
# item存儲數據
item['shop_name'] = shop_name
item['brand'] = brand
item['standard'] = standard
item['weight'] = weight
item['produce'] = produce
item['approval_num'] = approval_num
item['produce_type'] = produce_type
yield item
3、要建立一個Spider, 你必須用scrapy.Spider類創建一個子類,並確定了三個強制的屬性 和 一個方法。
- ① name ="":這個爬蟲的識別名稱,必須是唯一的,在不同的爬蟲必須定義不同的名字。
- ②allow_domains=[]是搜索的域名範圍,也就是爬蟲的約束區域,規定爬蟲只爬取這個域名下的網頁,不存在的URL會被忽略。
- ③ start_urls=():爬取的URL元祖/列表。爬蟲從這裏開始抓取數據,所以,第一次下載的數據將會從這些urls開始。其他子URL將會從這些起始URL中繼承性生成。
- ④ parse(self,response):解析的方法,每個初始URL完成下載後將被調用,調用的時候傳入從每一個URL傳回的Response對象來作爲唯一參數,主要作用如下:
負責解析返回的網頁數據(response.body),提取結構化數據(生成item) b) 生成需要下一頁的URL請求。
4、修改parse方法,添加parse_item方法
現在我們修改yao.py文件將start_urls的值修改爲需要爬取的初始url
start_urls = []
for i in range(1,51):
url = f'https://www.111.com.cn/categories/953710-j{i}.html'
start_urls.append(url)
這裏不使用response自帶的xpath,使用lxml xpath
from lxml import etree
5、然後運行一下看看,在yaowang目錄下執行:
scrapy crawl yao -o yao.csv
scrapy保存信息的最簡單的方法主要有四種,-o 輸出指定格式的文件,命令如下:
json格式,默認爲Unicode編碼
scrapy crawl yao -o yao.json
json lines格式,默認爲Unicode編碼
scrapy crawl yao -o yao.jsonl
csv 逗號表達式,可用Excel打開
scrapy crawl yao -o yao.csv
xml格式
scrapy crawl yao -o yao.xml
輸出日誌到指定文件,但是日誌就不會輸出到屏幕上了
scrapy crawl yao -s LOG_FILE=yao.log
6、利用pipelines.py:項目的管道文件進行數據的保存
mysql保存::
class YaowangPipeline(object):
def __init__(self):
# 建立數據庫連接
self.con = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='q1q1q1',
database='aabbcc',
charset='utf8')
# 創建操作遊標
self.cursor = self.con.cursor()
def process_item(self, item, spider):
print("="*30)
# data_list.append(dict(item))
# with open('yaowang.json','w',encoding='utf-8')as fp:
# json.dump(data_list,fp,ensure_ascii=False)
name = item['name']
href = item['href']
price = item['price']
store = item['store']
url = item['url']
shop_name = item['shop_name']
brand = item['brand']
standard = item['standard']
weight = item['weight']
produce = item['produce']
approval_num = item['approval_num']
produce_type = item['produce_type']
# 定義sql語句
# sql = "INSERT INTO yaowang_er(name,href,price,store,url,shop_name,brand,standard,weight,produce,approval_num,produce_type) values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
#
# # 執行sql語句
# self.cursor.execute(sql,(name,href,price,store,url,shop_name,brand,standard,weight,produce,approval_num,produce_type))
# # 保存修改
# self.con.commit()
return item
def __del__(self):
# 關閉操作遊標
self.cursor.close()
# 關閉數據庫連接
self.con.close()
MongoDB保存:
class YaowangMongoPipeline(object):
def __init__(self):
# 創建客戶端
self.client = pymongo.MongoClient('localhost')
# 創建數據庫
self.db = self.client['yaowang']
# 創建集合
self.collection = self.db['yao_info']
def process_item(self, item, spider):
print("="*30)
self.collection.insert(dict(item))
return item
然後settings.py 中使用管道文件配置,進行數據保存
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
# 'taoche.pipelines.TaochePipeline': 300,
'taoche.pipelines.TaocheMongoPipeline': 300,
}
運行爬蟲:
scrapy crawl yao