1. 高考派大學數據----寫在前面
終於寫到了scrapy
爬蟲框架了,這個框架可以說是python爬蟲框架里面出鏡率最高的一個了,我們接下來重點研究一下它的使用規則。
安裝過程自己百度一下,就能找到3種以上的安裝手法,哪一個都可以安裝上
可以參考 https://scrapy-chs.readthedocs.io/zh_CN/0.24/intro/install.html
官方說明進行安裝。
2. 高考派大學數據----創建scrapy項目
通用使用下面的命令,創建即可
scrapy startproject mySpider
完成之后,你的項目的目錄結構為
每個文件對應的意思為
- scrapy.cfg 項目的配置文件
- mySpider/ 根目錄
- mySpider/items.py 項目的目標文件,規范數據格式,用來定義解析對象對應的屬性或字段。
- mySpider/pipelines.py 項目的管道文件,負責處理被spider提取出來的item。典型的處理有清理、 驗證及持久化(例如存取到數據庫)
- mySpider/settings.py 項目的設置文件
- mySpider/spiders/ 爬蟲主目錄
- middlewares.py Spider中間件是在引擎及Spider之間的特定鈎子(specific hook),處理spider的輸入(response)和輸出(items及requests)。 其提供了一個簡便的機制,通過插入自定義代碼來擴展Scrapy功能。 本篇文章沒有涉及
高考派大學數據----創建Scrapy爬蟲
通過命令行進入到 mySpider/spiders/ 目錄,然后執行如下命令
scrapy genspider GaoKao "www.gaokaopai.com"
打開mySpider/spiders/ 目錄里面的 GaoKao,默認增加了 下列代碼
import scrapy
class GaoKaoSpider(scrapy.Spider):
name = "GaoKao"
allowed_domains = ["www.gaokaopai.com"]
start_urls = ['http://www.gaokaopai.com/']
def parse(self, response):
pass
默認生成的代碼,包含一個GaoKaoSpider
的類,並且這個類是用scrapy.Spider
繼承來的
而且默認實現了三個屬性和一個方法
name = "" 這個是爬蟲的名字,必須唯一,在不同的爬蟲需要定義不同的名字
allowed_domains = [] 域名范圍,限制爬蟲爬取當前域名下的網頁
start_urls =[] 爬取的URL元組/列表。爬蟲從這里開始爬取數據,第一次爬取的頁面就是從這里開始,其他的URL將會從這些起始的URL爬取的結果中生成
parse(self,response) 解析網頁的方法,每個初始URL完成下載后將調用,調用的時候傳入每一個初始URL返回的Response對象作為唯一參數,主要作用1、負責解析返回的網頁數據,response.body
2、生成下一頁的URL請求
高考派大學數據----第一個案例
我們要爬取的是高考派大學數據
數據為 http://www.gaokaopai.com/rank-index.html
頁面下部有一個加載更多,點擊抓取鏈接
尷尬的事情發生了,竟然是一個POST請求,本打算實現一個GET的,這回代碼量有點大了~

scrapy 模式是GET請求的,如果我們需要修改成POST,那么需要重寫Spider類的start_requests(self) 方法,並且不再調用start_urls里面的url了,所以,咱對代碼進行一些修改。重寫代碼之后,注意下面這段代碼
request = FormRequest(self.start_url,headers=self.headers,formdata=form_data,callback=self.parse)
FormRequest 需要引入模塊 from scrapy import FormRequest
self.start_url 寫上post請求的地址即可
formdata用來提交表單數據
callback調用網頁解析參數
最后的 yield request 表示這個函數是一個生成器
import scrapy
from scrapy import FormRequest
import json
from items import MyspiderItem
class GaokaoSpider(scrapy.Spider):
name = 'GaoKao'
allowed_domains = ['gaokaopai.com']
start_url = 'http://www.gaokaopai.com/rank-index.html'
def __init__(self):
self.headers = {
"User-Agent":"自己找個UA",
"X-Requested-With":"XMLHttpRequest"
}
# 需要重寫start_requests() 方法
def start_requests(self):
for page in range(0,7):
form_data = {
"otype": "4",
"city":"",
"start":str(25*page),
"amount": "25"
}
request = FormRequest(self.start_url,headers=self.headers,formdata=form_data,callback=self.parse)
yield request
def parse(self, response):
print(response.body)
print(response.url)
print(response.body_as_unicode())
我們在 def parse(self, response):
函數里面,輸出一下網頁內容,這個地方,需要用到1個知識點是
獲取網頁內容 response.body
response.body_as_unicode()
- response.url獲取抓取的rul
- response.body獲取網頁內容字節類型
- response.body_as_unicode()獲取網站內容字符串類型
我們接下來就可以運行一下爬蟲程序了
在項目根目錄創建一個begin.py
文件,里面寫入如下代碼
from scrapy import cmdline
cmdline.execute(("scrapy crawl GaoKao").split())
運行該文件,記住在scrapy中的其他py文件中,運行是不會顯示相應的結果的,每次測試的時候,都需要運行begin.py 當然,你可起一個其他的名字。
如果你不這么干的,那么你只能 采用下面的操作,就是比較麻煩。
cd到爬蟲目錄里執行scrapy crawl GaoKao--nolog命令
說明:scrapy crawl GaoKao(GaoKao表示爬蟲名稱) --nolog(--nolog表示不顯示日志)

運行起來,就在控制台打印數據了,測試方便,可以把上述代碼中那個數字7,修改成2,有心人能看到我這個小文字
pycharm在運行過程中,會在控制台打印很多紅色的字,沒事,那不是BUG
一定要在紅色的字中間找到黑色的字,黑色的字才是你打印出來的數據,如下,得到這樣的內容,就成功一大半了。
但是這個地方有個小坑,就是,你會發現返回的數據不一致,這個我測試了一下,是因為第一頁的數據返回的不是JSON格式的,而是普通的網頁,那么我們需要針對性處理一下,這個先不用管,我們把items.py
進行完善
import scrapy
class MyspiderItem(scrapy.Item):
# 學校名稱
uni_name = scrapy.Field()
uni_id = scrapy.Field()
city_code = scrapy.Field()
uni_type = scrapy.Field()
slogo = scrapy.Field()
# 錄取難度
safehard = scrapy.Field()
# 院校所在地
rank = scrapy.Field()
然后在剛才的GaokaoSpider類中,繼續完善parse函數,通過判斷 response.headers["Content-Type"]
去確定本頁面是HTML格式,還是JSON格式。
if(content_type.find("text/html")>0):
# print(response.body_as_unicode())
trs = response.xpath("//table[@id='results']//tr")[1:]
for item in trs:
school = MyspiderItem()
rank = item.xpath("td[1]/span/text()").extract()[0]
uni_name = item.xpath("td[2]/a/text()").extract()[0]
safehard = item.xpath("td[3]/text()").extract()[0]
city_code = item.xpath("td[4]/text()").extract()[0]
uni_type = item.xpath("td[6]/text()").extract()[0]
school["uni_name"] = uni_name
school["uni_id"] = ""
school["city_code"] = city_code
school["uni_type"] = uni_type
school["slogo"] = ""
school["rank"] = rank
school["safehard"] = safehard
yield school
else:
data = json.loads(response.body_as_unicode())
data = data["data"]["ranks"] # 獲取數據
for item in data:
school = MyspiderItem()
school["uni_name"] = item["uni_name"]
school["uni_id"] = item["uni_id"]
school["city_code"] = item["city_code"]
school["uni_type"] = item["uni_type"]
school["slogo"] = item["slogo"]
school["rank"] = item["rank"]
school["safehard"] = item["safehard"]
# 將獲取的數據交給pipelines,pipelines在settings.py中定義
yield school
parse() 方法的執行機制
- 使用yield返回數據,不要使用return。這樣子parse就會被當做一個生成器。scarpy將parse生成的數據,逐一返回
- 如果返回值是request則加入爬取隊列,如果是item類型,則交給pipeline出來,其他類型報錯
到這里,如果想要數據准備的進入到 pipeline 中,你需要在setting.py
中將配置開啟
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'mySpider.pipelines.MyspiderPipeline': 300,
}
同時編寫 pipeline.py
文件
import os
import csv
class MyspiderPipeline(object):
def __init__(self):
# csv 文件
store_file = os.path.dirname(__file__)+"/spiders/school1.csv"
self.file = open(store_file,"a+",newline='',encoding="utf-8")
self.writer = csv.writer(self.file)
def process_item(self, item, spider):
try:
self.writer.writerow((
item["uni_name"],
item["uni_id"],
item["city_code"],
item["uni_type"],
item["slogo"],
item["rank"],
item["safehard"]
))
except Exception as e:
print(e.args)
def close_spider(self,spider):
self.file.close()
好了,代碼全部編寫完畢,還是比較簡單的吧,把上面的數字在修改成7,為啥是7,因為只能獲取到前面150條數據
