scrapy 爬蟲流程
scrapy 爬蟲流程和一般的爬蟲流程基本一樣,發送url,響應提取url和數據,數據存儲,url重新放到url隊列中
Scrapy Engine(引擎) | 總指揮:負責數據和信號在不同模塊之間傳遞 | scrapy實現 |
Scheduler(調度器) | 隊列,存放engine發送過來的request請求 | scrapy實現 |
Downloader(下載器) | 下載引擎發送過來的請求,並返回給引擎 | scrapy實現 |
Spider(爬蟲) | 處理引擎發送過來的reponse,提取數據,url,返回給引擎 | 自己手寫 |
Item Pipeline(管道) | 處理引擎傳送過來的數據,存儲到mongodb | 自己手寫 |
Downloader Middlewares (下載中間件) |
自定義下載擴展,例:設置代理IP,user-agent | 自己寫相關類 |
Spider Middlewares (爬蟲中間件) |
可自定義request請求和進行response過濾 | 一般不用手寫 |
scrapy 工程創建
創建工程
scrapy startproject +項目名字
$ scrapy startproject spider_boss
創建爬蟲
$ cd spider_boss
scrapy genspider +<爬蟲名字> + <允許爬取的域名>
$ scrapy genspider zhipin zhipin.com
oniondeMacBook-Pro:spider onion$ tree spider_boss/ spider_boss/ ├── README.md ├── main.py 自己寫的爬蟲執行入口 ├── scrapy.cfg └── spider_boss ├── __init__.py ├── items.py 定義自己需要爬取的內容 ├── middlewares.py 下載中間件添加代理 ├── pipelines.py 管道處理數據存儲 ├── settings.py 爬蟲的設置文件,下載延時,日誌等級等 └── spiders 自己定義的spider文件夾 ├── __init__.py ├── zhipin.py └── zhipin_test.py
scrapy文件解析
spider_boss.items.py
#spider_boss.items.py
class SpiderBossItem(scrapy.Item):
# define the fields for your item here like:
#需要爬取得內容
job_title = scrapy.Field() #一個字典
company = scrapy.Field()
money = scrapy.Field()
job_context = scrapy.Field()
detail_url=scrapy.Field()
#獲取數據的時候,使用不同的item來存放不同的數據
#在數據交給pipeline 的時候,可以通過isnstance(item,SpiderBossItem)來判斷屬於哪個item,進行不同的數據(item)處理
spiders.zhipin.py
從選擇器中提取字符串
1.extract() 返回一個包含有字符串數據的列表
2.extract_first() 返回列表中的第一個字符串
注意:
spider中的parse方法名不能修改
爬取得url必須是屬於allowed_domains下的鏈接
response.xpath()返回的是一個含有selector對象的列表
scrapy.request():
scrapy.Request(url,[callback,meta,dont_filter=False,...]) 常用參數: callback:指定傳入的url交給哪個解析函數去處理 meta:實現在不同解析函數中傳遞數據,meta 會默認帶部分信息,如下載延遲,請求深度(settings中配置)等 dont_filter:讓scrapy 是否過濾當前url,默認False,帶去重功能,如果需要重複請求某個url,需要置True #scrap.Request()函數定義 class Request(object_ref): def __init__(self, url, callback=None, method='GET', headers=None, body=None, cookies=None, meta=None, encoding='utf-8', priority=0, dont_filter=False, errback=None, flags=None):
#spiders.zhiping.py
class ZhipinSpider(scrapy.Spider):
name = 'zhipin' #爬蟲名 <scrapy crawl zhipin>
allowed_domains = ['zhipin.com'] #允許爬的域名,防止爬到其他網站
start_urls = ['http://zhipin.com/job_detail/?query=python'] #開始爬取的地址
def parse(self, response): #接收下載中間件傳來的response
info_list = response.xpath('//div[@class="job-list"]//li')
for each in info_list:
item = SpiderBossItem() #item中定義需要爬去的內容
item["company"] = each.xpath('.//div[@class="company-text"]/h3//text()').extract_first()
item["job_title"] = each.xpath('.//div[@class="job-title"]/text()').extract_first()
item["money"] = each.xpath(".//span/text()").extract_first()
item["detail_url"] = each.xpath('.//div[@class="job-title"]/../@href').extract_first()
item["detail_url"] = "https://www.zhipin.com" + item["detail_url"]
# logger.warning(item)
# 回調處理職位詳情頁
yield scrapy.Request(
item["detail_url"],
callback=self.parse_context_detail,
meta={"item": item}# 將這裏的item傳送給parse_context_detail中繼續處理,
#這裏傳過去的是同一個item,如果你不想下次的處理影響當前item,使用deepcopy 例: meta={"item":deepcopy(item)}
)
# next page
next_url = response.xpath("//a[@class='next']/@href").extract_first()
if next_url is not None:
next_url = "https://www.zhipin.com" + next_url
yield scrapy.Request(
next_url,
callback=self.parse
)
def parse_context_detail(self, response):
item = response.meta["item"] #接收parse中的item
item["job_context"] = response.xpath("//div[@class='job-sec']/div//text()").extract()
# print(item)
yield item #傳送個管道處理數據
spider_boss.pipelines.py
管道權重越小,優先級越高
pipeline process_item()方法不能修改爲其他名稱
#spider_boss.pipelines.py
class SpiderBossPipeline(object):
def open_spider(self, spider): #爬蟲開始會調用一次
#mongodb的配置個可以放在settings.py文件中管理
self.client = MongoClient("mongodb://127.0.0.1:27017/boss")
def close_spider(self, spider): #爬蟲結束會調用
print("close ...")
print(spider.name)
self.client.close()
def process_item(self, item, spider): #item參數 spider yield item 過來的
if spider.name =="zhipin":
self.collection = self.client["boss"]["python"]
item["job_context"] = self.process_context(item["job_context"])
# collection.insert(item)
if isinstance(item, SpiderBossItem):
self.collection.insert(dict(item)) #數據存儲到mongodb中
# print(item)
return item
def process_context(self, context):
# 處理 "\n" 和空字符串
context = [i.strip() for i in context if i.strip() != ""]
return context
管道文件寫完後需要在settings.py中開啓管道
spider_boss.settings.py中
#spider_boss.settings
#: 300 值越小優先級越高
ITEM_PIPELINES = {
'spider_boss.pipelines.SpiderBossPipeline': 300,
'spider_boss.pipelines.SpiderBossPipeline1': 301,
}
spider_boss.middlewares.py
from scrapy import signals
import random
from scrapy.utils.project import get_project_settings
settings = get_project_settings()
class MyUserAgentMiddleware(object): #設置user-agent
def __init__(self):
pass
def process_request(self, request, spider):
# spider.logger.info(msg='now entring download midware')
agent = random.choice(settings.get('MY_USER_AGENT_LIST'))
if agent:
request.headers.setdefault(b'User-Agent',agent)
spider.logger.info(u'User-Agent is : {} {}'.format(request.headers.get('User-Agent'), request))
class IPProxyDownloadMiddleware(object): #設置IP代理
def process_request(self, request, spider):
ip_proxy = random.choice(settings.get('IP_PROXY_LIST')) #獲取settings文件中ip代理列表
print('*'*20)
print(ip_proxy)
#添加代理需要在request的meta信息中添加proxy字段,
#代理形式:協議+ip+端口
request.meta["proxy"]=ip_proxy
spider_boss.settings.py中開啓下載中間件和定義相關的user-agent列表和代理ip列表
MY_USER_AGENT_LIST=[
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
...
]
IP_PROXY_LIST=[
# "http://58.253.155.211:9999",
"https://61.189.242.243:55484",
# "http://175.44.158.72:9000"
]
DOWNLOADER_MIDDLEWARES = {
'spider_boss.middlewares.MyUserAgentMiddleware': 543,
'spider_boss.middlewares.IPProxyDownloadMiddleware': 542,
}