目標網站:http://images.so.com/ (攝影專欄)
Scrapy抓站:360照片
1. 新建項目
scrapy startproject images360(名目名)
2. 新建Spider
直接用Pycharm打開上一步所創建的項目,在最下面的Terminal處執行該命令:
scrapy genspider images images.so.com
3. 分析目標網站的種種
- 進入目標網站所要爬取的攝影專題的頁面下
- 下拉滾動欄查看圖片,顯而易見可知是Ajax請求數據的方式,我以前的博客中有對該請求方式的詳細說明,f12直接查看XHR下的請求信息:
可以看到請求方式是GET方式,其是帶有參數的請求方式,分析參數得:
ch指所選的專題名稱,sn指該頁顯示30張圖片,listtype指一種排序方式,temp指不知名參數,可變參數只有sn,所以該參數作爲翻頁更新請求的突破點。 - 查看數據所在地及其返回格式:
可得數據返回格式爲JSON,很好處理。
4. 構造請求
在 images.py 文件下重寫定義 start_requests() 方法:
# 此方法用於生成初始請求,它必須返回一個迭代的對象。
# 此方法會默認使用 start_urls 中的URL來構造 Request,而且 Request 是GET請求方式
# 若啓動時以 POST 請求方式來訪問某個站點,可以直接重寫這個方法,發送 POST請求時,使用 FormRequest 即可
def start_requests(self):
# 設置請求頭,因爲之 get 請求,其請求參數跟在 url 地址後 ,可以這樣設置
data = {
'ch':'photography',
'listtype':'new'
}
base_url = 'https://image.so.com/zjl?'
# 生成 50 次的請求
for page in range(1,self.settings.get('MAX_PAGE')+1):
data['sn'] = page * 30
# 使用 urlencode() 方法,將字典轉換爲 url 的 GET 參數
params = urlencode(data)
url = base_url + params
print(url)
yield Request(url,self.parse)
在 setting.py 中定義最大爬取頁數的變量,以及修改 ROBOTSTXT_OBEY 的值:
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# 爬取的頁數
MAX_PAGE = 50
5. 定義提取信息的字段
修改 Item.py ,定義自己的字段類:
import scrapy
from scrapy import Field
class ImageItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
collection = table = 'images'
# 照片的 id
id = Field()
# 照片類型
pic_desc = Field()
# 照片地址
url = Field()
# 照片名字
title = Field()
# 照片縮略圖
thumb = Field()
6. 編寫 Spider 方法
修改 images.py ,重寫 parse() 方法:
def parse(self, response):
result = json.loads(response.text)
for image in result.get('list'):
item = ImageItem()
item['id'] = image.get('id')
item['pic_desc'] = image.get('pic_desc')
item['title'] = image.get('title')
item['url'] = image.get('qhimg_url')
item['thumb'] = image.get('qhimg_thumb')
yield item
7. 存儲信息
7.1 Mysql保存
執行該功能的是 Item Pipeline 項目管道,所以在 pipelines.py 中重寫方法並配置連接數據庫的相關信息:
- 建立數據庫,表,字段等信息
- 實現自己 PymysqlPipeline
class PymysqlPipeline(object):
#連接數據庫
def __init__(self):
self.connect = pymysql.connect(
host = 'localhost',
database = 'image_360',
user = 'root',
password = '123456',
charset = 'utf8',
port = 3306
)
# 創建遊標對象
self.cursor = self.connect.cursor()
# 此方法是必須要實現的方法,被定義的 Item Pipeline 會默認調用這個方法對 Item 進行處理
def process_item(self,item,spider):
cursor = self.cursor
sql = 'insert into image_information(id,pic_desc,title,url,thumb) values (%s,%s,%s,%s,%s)'
cursor.execute(sql,(
item['id'],item['pic_desc'],item['title'],item['url'],item['thumb']
))
# 提交數據庫事務
self.connect.commit()
return item
7.2 本地文件保存
同樣在 pipelines.py 添加如下的代碼即可:
# 文件下載和圖片下載,支持異步和多線程
class ImagePipeline(ImagesPipeline):
# 該方法返回保存的每張圖片的名字
def file_path(self, request, response=None, info=None):
url = request.url
file_name = url.split('/')[-1]
return file_name
# 單個 Item 完成下載時的處理方法,並不是每張圖片都可以下載成功,所以如果某張圖片下載失敗就不需要保存到數據庫
def item_completed(self, results, item, info):
image_path = [x['path'] for ok,x in results if ok]
if not image_path:
raise DropItem('Image Downloaded Failed')
return item
def get_media_requests(self, item, info):
yield Request(item['url'])
8. 執行程序
在最下面的Terminal處執行該命令:
scrapy crawl images
9. 最終的效果圖
以上就是整個的流程,比昨天有學到的新東西。