Scrapy是用純python實(shí)現(xiàn)一個為了爬取網(wǎng)站數(shù)據(jù)齿坷、提取結(jié)構(gòu)性數(shù)據(jù)而編寫的應(yīng)用框架,用途非常廣泛
-
Scrapy架構(gòu)圖(綠線是數(shù)據(jù)流向):
image Scrapy Engine(引擎): 負(fù)責(zé)Spider、ItemPipeline剥悟、Downloader黔漂、Scheduler中間的通訊,信號民效、數(shù)據(jù)傳遞等憔维。
Scheduler(調(diào)度器): 它負(fù)責(zé)接受引擎發(fā)送過來的Request請求,并按照一定的方式進(jìn)行整理排列畏邢,入隊(duì)业扒,當(dāng)引擎需要時,交還給引擎舒萎。
Downloader(下載器):負(fù)責(zé)下載Scrapy Engine(引擎)發(fā)送的所有Requests請求程储,并將其獲取到的Responses交還給Scrapy Engine(引擎),由引擎交給Spider來處理
Spider(爬蟲):它負(fù)責(zé)處理所有Responses,從中分析提取數(shù)據(jù)臂寝,獲取Item字段需要的數(shù)據(jù)虱肄,并將需要跟進(jìn)的URL提交給引擎,再次進(jìn)入Scheduler(調(diào)度器)交煞,
Item Pipeline(管道):它負(fù)責(zé)處理Spider中獲取到的Item咏窿,并進(jìn)行進(jìn)行后期處理(詳細(xì)分析、過濾素征、存儲等)的地方.
Downloader Middlewares(下載中間件):你可以當(dāng)作是一個可以自定義擴(kuò)展下載功能的組件集嵌。
Spider Middlewares(Spider中間件):你可以理解為是一個可以自定擴(kuò)展和操作引擎和Spider中間通信的功能組件(比如進(jìn)入Spider的Responses;和從Spider出去的Requests)
一萝挤、新建項(xiàng)目(scrapy startproject)
- 創(chuàng)建爬蟲項(xiàng)目
scrapy startproject 項(xiàng)目名稱 (有些用戶需要前面加上python -m)
- 新建爬蟲文件
scrapy genspider 爬蟲文件 域名
- item 定義結(jié)構(gòu)化數(shù)據(jù)字段,用來保存爬取到的數(shù)據(jù)根欧,有點(diǎn)像Python中的dict怜珍,但是提供了一些額外的保護(hù)減少錯誤。
item文件
class JobboleItem(scrapy.Item):
# define the fields for your item here like:
#標(biāo)題
title = scrapy.Field()
#創(chuàng)建時間
create_date = scrapy.Field()
#文章地址
url = scrapy.Field()
#id
url_object_id = scrapy.Field()
#文章圖片
front_image_url = scrapy.Field()
#文章圖片地址
front_image_path = scrapy.Field()
# 點(diǎn)贊數(shù)
praise_nums = scrapy.Field()
#收藏?cái)?shù)
bookmark_nums = scrapy.Field()
二凤粗、制作爬蟲
# -*- coding: utf-8 -*-
import scrapy
class JobboleSpider(scrapy.Spider):
name = '爬蟲文件'
allowed_domains = ['網(wǎng)站域名']
start_urls = ['https://xxx.xxxx.com/xxxx-x/']
def parse(self, response):
pass
name = " " :這個爬蟲的識別名稱酥泛,必須是唯一的,在不同的爬蟲必須定義不同的名字嫌拣。
allow_domains = [] 是搜索的域名范圍柔袁,也就是爬蟲的約束區(qū)域,規(guī)定爬蟲只爬取這個域名下的網(wǎng)頁异逐,不存在的URL會被忽略捶索。
start_urls = () :爬取的URL元祖/列表。爬蟲從這里開始抓取數(shù)據(jù)灰瞻,所以腥例,第一次下載的數(shù)據(jù)將會從這些urls開始。其他子URL將會從這些起始URL中繼承性生成酝润。
parse(self, response) :解析的方法燎竖,每個初始URL完成下載后將被調(diào)用,調(diào)用的時候傳入從每一個URL傳回的Response對象來作為唯一參數(shù)
三要销、在parse方法中做數(shù)據(jù)的提取
from jobboleproject.items import JobboleprojectItem
1.獲取圖片和文章詳情的鏈接
def parse(self, response):
# css選擇器獲取當(dāng)前列表頁面的所有的節(jié)點(diǎn)
post_nodes = response.css("#archive .floated-thumb .post-thumb a")
# 如果不是完整的域名 需要拼接完整 response.url + post_url
# 獲取到的URL可能不是一個域名构回,也可能是具體的文章需要使用parse函數(shù)from urllib import parse
for post_node in post_nodes:
image_url = post_node.css("img::attr(src)").extract_first("")
post_url = post_node.css("::attr(href)").extract_first("")
full_url = response.urljoin(post_url)
#meta參數(shù)對應(yīng)的是一個字典,用來傳遞數(shù)據(jù)
yield scrapy.Request(url=full_url, meta={"front_image_url": image_url},
callback=self.parse_detail)
2.然后將我們得到的數(shù)據(jù)封裝到一個 JobboleItem 對象中蕉陋,可以保存每個文章的屬性:
def parse_detail(self,response):
# print(response)
# 使用xpath語法或者css語法提取網(wǎng)頁的相關(guān)信息
# extract() 串行化并將匹配到的節(jié)點(diǎn)返回一個unicode字符串列表
item = JobboleprojectItem()
#標(biāo)題
item['title'] = response.xpath('//div[@class="entry-header"]/h1/text()').extract_frist("")
#發(fā)布日期
item['create_date'] = response.xpath("http://p[@class='entry-meta-hide-on-mobile']/text()").extract_first("").strip().replace("·","").strip()
#文章地址詳情地址
item['url'] = response.url
# http://blog.jobbole.com/113949/
#文章的id
item['url_object_id'] = re.match(".*?(\d+).*", url).group(1)
# 文章圖片
item['front_image_url'] = response.meta.get("front_image_url","")
# 點(diǎn)贊數(shù)
item['praise_nums'] = response.xpath("http://span[contains(@class,'vote-post-up')]/h10/text()").extract_first("")
# 收藏?cái)?shù)
bookmark_nums = response.xpath("http://span[contains(@class,'bookmark-btn')]/text()").extract_first("")
match_bookmark_nums = re.match(".*?(\d+).*",bookmark_nums)
if match_bookmark_nums:
item['bookmark_nums'] = int(match_bookmark_nums.group(1))
else:
item['bookmark_nums'] = 0
# 評論數(shù)
comment_nums = response.xpath("http://a[@href='#article-comment']/span/text()").extract_first("")
match_comment_nums = re.match(".*?(\d+).*",comment_nums)
if match_comment_nums:
item['comment_nums'] = int(match_comment_nums.group(1))
else:
item['comment_nums'] = 0
# 文章內(nèi)容
item['content'] = response.xpath("http://div[@class='entry']").extract_first("")
# 過濾評論標(biāo)簽
tag_list = response.xpath("http://p[@class='entry-meta-hide-on-mobile']//a/text()").extract()
tag_list = [element for element in tag_list if not element.strip().endswith("評論")]
# 標(biāo)簽
item['tags'] = ",".join(tag_list)
print(item)
# return返回?cái)?shù)據(jù)捐凭,不經(jīng)過pipelines
# return item
# yield將獲取的數(shù)據(jù)交給pipelines
# yield item
簡單地講一下:
yield 的作用就是把一個函數(shù)變成一個 generator(生成器),帶有 yield 的函數(shù)不再是一個普通函數(shù)凳鬓,Python 解釋器會將其視為一個 generator茁肠。
四、編寫Item Pipeline
import something
class SomethingPipeline(object):
def __init__(self):
# 可選實(shí)現(xiàn)缩举,做參數(shù)初始化等
# doing something
def process_item(self, item, spider):
# item (Item 對象) – 被爬取的item
# spider (Spider 對象) – 爬取該item的spider
# 這個方法必須實(shí)現(xiàn)垦梆,每個item pipeline組件都需要調(diào)用該方法,
# 這個方法必須返回一個 Item 對象仅孩,被丟棄的item將不會被之后的pipeline組件所處理托猩。
return item
def open_spider(self, spider):
# spider (Spider 對象) – 被開啟的spider
# 可選實(shí)現(xiàn),當(dāng)spider被開啟時辽慕,這個方法被調(diào)用京腥。
def close_spider(self, spider):
# spider (Spider 對象) – 被關(guān)閉的spider
# 可選實(shí)現(xiàn),當(dāng)spider被關(guān)閉時溅蛉,這個方法被調(diào)用
管道作用:
驗(yàn)證爬取的數(shù)據(jù)(檢查item包含某些字段公浪,比如說name字段)
查重(并丟棄)
將爬取結(jié)果保存到文件或者數(shù)據(jù)庫中
五他宛、重新啟動爬蟲
scrapy crawl jobbole