python 爬蟲學習過程剖析

目錄

一 python簡介

二 Python爬蟲過程圖和學習路線

三 爬蟲過程代碼

四 解析庫

1.JSON解析

2.網頁解析

五 33個爬蟲項目實戰

六 總結

 參考資料




一 python簡介

Python是著名的“龜叔”Guido van Rossum在1989年聖誕節期間,爲了打發無聊的聖誕節而編寫的一個編程語言。

創始人Guido van Rossum是BBC出品英劇Monty Python’s Flying Circus(中文:蒙提·派森的飛行馬戲團)的狂熱粉絲,因而將

自己創造的這門編程語言命名爲Python。

二 Python爬蟲過程圖和學習路線

 

三 爬蟲過程代碼

init.py

# -*- coding: UTF-8 -*-  
 
# import need manager module  
import MongoUtil  
import FileUtil  
import conf_dev  
import conf_test  
import scratch_airport_name  
import scratch_flight_number  
import scratch_movie_name  
import scratch_train_number  
import scratch_train_station  
import MainUtil

conf_dev.py

# -*- coding: UTF-8 -*-  
# the configuration file of develop environment  
 
# path configure  
data_root_path = 'E:/test/smart/data'  
 
# mongodb configure  
user = "cby"  
pwd = "123456"  
server = "localhost"  
port = "27000"  
db_name = "testdb"

將更新的內容插入mongodb中

MongoUtil.py

# -*- coding: UTF-8 -*-  
 
import platform  
from pymongo import MongoClient  
from datetime import datetime, timedelta, timezone  
import conf_dev  
import conf_test  
 
# configure Multi-confronment  
platform_os = platform.system()  
config = conf_dev  
if (platform_os == 'Linux'):  
    config = conf_test  
# mongodb  
uri = 'mongodb://' + config.user + ':' + config.pwd + '@' + config.server + ':' + config.port + '/' + config.db_name  
 
 
# 將數據寫入mongodb  
# @author chenmc  
# @param uri connect to mongodb  
# @path save mongodb field  
# @data save mongodb field  
# @operation save mongodb field default value 'append'  
# @date 2017/12/07 16:30  
# 先在mongodb中插入一條自增數據 db.sequence.insert({ "_id" : "version","seq" : 1})  
 
def insert(path, data, operation='append'):  
    client = MongoClient(uri)  
    resources = client.smartdb.resources  
    sequence = client.smartdb.sequence  
    seq = sequence.find_one({"_id": "version"})["seq"]      #獲取自增id  
    sequence.update_one({"_id": "version"}, {"$inc": {"seq": 1}})       #自增id+1  
    post_data = {"_class": "com.gionee.smart.domain.entity.Resources", "version": seq, "path": path,  
                 "content": data, "status": "enable", "operation": operation,  
                 "createtime": datetime.now(timezone(timedelta(hours=8)))}  
    resources.insert(post_data)     #插入數據
 

下面真正的執行方法來了,這五個py分別表示爬取五種信息:機場名、航班號、電影名、列車號、列車站。他們的結構都差不多,如下:

第一部分:定義查找的url;
第二部分:獲取並與舊數據比較,返回新數據;
第三部分:main方法,執行寫入新數據到文件和mongodb中;
scratch_airport_name.py:爬取全國機場

# -*- coding: UTF-8 -*-  
import requests  
import bs4  
import json  
import MainUtil  
 
resources_file_path = '/resources/airplane/airportNameList.ini'  
scratch_url_old = 'https://data.variflight.com/profiles/profilesapi/search'  
scratch_url = 'https://data.variflight.com/analytics/codeapi/initialList'  
get_city_url = 'https://data.variflight.com/profiles/Airports/%s'  
 
 
#傳入查找網頁的url和舊數據,然後本方法會比對原數據中是否有新的條目,如果有則不加入,如果沒有則重新加入,最後返回新數據
def scratch_airport_name(scratch_url, old_airports):  
    new_airports = []  
    data = requests.get(scratch_url).text  
    all_airport_json = json.loads(data)['data']  
    for airport_by_word in all_airport_json.values():  
        for airport in airport_by_word:  
            if airport['fn'] not in old_airports:  
                get_city_uri = get_city_url % airport['id']  
                data2 = requests.get(get_city_uri).text  
                soup = bs4.BeautifulSoup(data2, "html.parser")  
                city = soup.find('span', text="城市").next_sibling.text  
                new_airports.append(city + ',' + airport['fn'])  
    return new_airports  
 
 #main方法,執行這個py,默認調用main方法,相當於java的main
if __name__ == '__main__':  
    MainUtil.main(resources_file_path, scratch_url, scratch_airport_name)
scratch_flight_number.py:爬取全國航班號

#!/usr/bin/python  
# -*- coding: UTF-8 -*-  
 
import requests  
import bs4  
import MainUtil  
 
resources_file_path = '/resources/airplane/flightNameList.ini'  
scratch_url = 'http://www.variflight.com/sitemap.html?AE71649A58c77='  
 
 
def scratch_flight_number(scratch_url, old_flights):  
    new_flights = []  
    data = requests.get(scratch_url).text  
    soup = bs4.BeautifulSoup(data, "html.parser")  
    a_flights = soup.find('div', class_='list').find_all('a', recursive=False)  
    for flight in a_flights:  
        if flight.text not in old_flights and flight.text != '國內航段列表':  
            new_flights.append(flight.text)  
    return new_flights  
 
 
if __name__ == '__main__':  
    MainUtil.main(resources_file_path, scratch_url, scratch_flight_number)
scratch_movie_name.py:爬取最近上映的電影

#!/usr/bin/python  
# -*- coding: UTF-8 -*-  
import re  
import requests  
import bs4  
import json  
import MainUtil  
 
# 相對路徑,也是需要將此路徑存入數據庫  
resources_file_path = '/resources/movie/cinemaNameList.ini'  
scratch_url = 'http://theater.mtime.com/China_Beijing/'  
 
 
# scratch data with define url  
def scratch_latest_movies(scratch_url, old_movies):  
    data = requests.get(scratch_url).text  
    soup = bs4.BeautifulSoup(data, "html.parser")  
    new_movies = []  
    new_movies_json = json.loads(  
        soup.find('script', text=re.compile("var hotplaySvList")).text.split("=")[1].replace(";", ""))  
    coming_movies_data = soup.find_all('li', class_='i_wantmovie')  
    # 上映的電影  
    for movie in new_movies_json:  
        move_name = movie['Title']  
        if move_name not in old_movies:  
            new_movies.append(movie['Title'])  
    # 即將上映的電影  
    for coming_movie in coming_movies_data:  
        coming_movie_name = coming_movie.h3.a.text  
        if coming_movie_name not in old_movies and coming_movie_name not in new_movies:  
            new_movies.append(coming_movie_name)  
    return new_movies  
 
 
if __name__ == '__main__':  
    MainUtil.main(resources_file_path, scratch_url, scratch_latest_movies)
scratch_train_number.py:爬取全國列車號

#!/usr/bin/python  
# -*- coding: UTF-8 -*-  
import requests  
import bs4  
import json  
import MainUtil  
 
resources_file_path = '/resources/train/trainNameList.ini'  
scratch_url = 'http://www.59178.com/checi/'  
 
 
def scratch_train_number(scratch_url, old_trains):  
    new_trains = []  
    resp = requests.get(scratch_url)  
    data = resp.text.encode(resp.encoding).decode('gb2312')  
    soup = bs4.BeautifulSoup(data, "html.parser")  
    a_trains = soup.find('table').find_all('a')  
    for train in a_trains:  
        if train.text not in old_trains and train.text:  
            new_trains.append(train.text)  
    return new_trains  
 
 
if __name__ == '__main__':  
    MainUtil.main(resources_file_path, scratch_url, scratch_train_number)
scratch_train_station.py:爬取全國列車站

#!/usr/bin/python  
# -*- coding: UTF-8 -*-  
import requests  
import bs4  
import random  
import MainUtil  
 
resources_file_path = '/resources/train/trainStationNameList.ini'  
scratch_url = 'http://www.smskb.com/train/'  
 
 
def scratch_train_station(scratch_url, old_stations):  
    new_stations = []  
    provinces_eng = (  
        "Anhui", "Beijing", "Chongqing", "Fujian", "Gansu", "Guangdong", "Guangxi", "Guizhou", "Hainan", "Hebei",  
        "Heilongjiang", "Henan", "Hubei", "Hunan", "Jiangsu", "Jiangxi", "Jilin", "Liaoning", "Ningxia", "Qinghai",  
        "Shandong", "Shanghai", "Shanxi", "Shanxisheng", "Sichuan", "Tianjin", "Neimenggu", "Xianggang", "Xinjiang",  
        "Xizang",  
        "Yunnan", "Zhejiang")  
    provinces_chi = (  
        "安徽", "北京", "重慶", "福建", "甘肅", "廣東", "廣西", "貴州", "海南", "河北",  
        "黑龍江", "河南", "湖北", "湖南", "江蘇", "江西", "吉林", "遼寧", "寧夏", "青海",  
        "山東", "上海", "陝西", "山西", "四川", "天津", "內蒙古", "香港", "新疆", "西藏",  
        "雲南", "浙江")  
    for i in range(0, provinces_eng.__len__(), 1):  
        cur_url = scratch_url + provinces_eng[i] + ".htm"  
        resp = requests.get(cur_url)  
        data = resp.text.encode(resp.encoding).decode('gbk')  
        soup = bs4.BeautifulSoup(data, "html.parser")  
        a_stations = soup.find('left').find('table').find_all('a')  
        for station in a_stations:  
            if station.text not in old_stations:  
                new_stations.append(provinces_chi[i] + ',' + station.text)  
    return new_stations  
 
 
if __name__ == '__main__':  
    MainUtil.main(resources_file_path, scratch_url, scratch_train_station)
 

下面這個main方法控制着執行流程,其他的執行方法調用這個main方法

MainUtil.py

# -*- coding: UTF-8 -*-  
 
import sys  
from datetime import datetime  
import MongoUtil  
import FileUtil  
 
# @param resources_file_path 資源文件的path  
# @param base_url 爬取的連接  
# @param scratch_func 爬取的方法  
def main(resources_file_path, base_url, scratch_func):  
    old_data = FileUtil.read(resources_file_path)   #讀取原資源  
    new_data = scratch_func(base_url, old_data)     #爬取新資源  
    if new_data:        #如果新數據不爲空  
        date_new_data = "//" + datetime.now().strftime('%Y-%m-%d') + "\n" + "\n".join(new_data) + "\n"      #在新數據前面加上當前日期  
        FileUtil.append(resources_file_path, date_new_data)     #將新數據追加到文件中  
        MongoUtil.insert(resources_file_path, date_new_data)    #將新數據插入到mongodb數據庫中  
    else:   #如果新數據爲空,則打印日誌  
        print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), '----', getattr(scratch_func, '__name__'), ": nothing to update ")
 

下面文件是一個util文件,主要是讀取原文件的內容,還有將新內容寫入原文件。

FileUtil.py

# -*- coding: UTF-8 -*-  
import conf_dev  
import conf_test  
import platform  
 
 
# configure Multi-confronment  
# 判斷當前系統,並引入相對的配置文件
platform_os = platform.system()  
config = conf_dev  
if (platform_os == 'Linux'):  
    config = conf_test  
# path  
data_root_path = config.data_root_path  
 
 
# load old data  
def read(resources_file_path, encode='utf-8'):  
    file_path = data_root_path + resources_file_path  
    outputs = []  
    for line in open(file_path, encoding=encode):  
        if not line.startswith("//"):  
            outputs.append(line.strip('\n').split(',')[-1])  
    return outputs  
# append new data to file from scratch  
def append(resources_file_path, data, encode='utf-8'):  
    file_path = data_root_path + resources_file_path  
    with open(file_path, 'a', encoding=encode) as f:  
        f.write(data)  
    f.close

到此結束。

從過以上例子主要讓大家能理解爬蟲整個實戰過程。

四 解析庫

 

1.JSON解析

一般情況下,網站會有純數據的接口和返回網頁的接口之分。因爲前後端分離的流行,所以越來越多的純數據接口了。純數據接口解析起來也會比網頁要簡單很多,所以豬哥建議我們在爬取數據的時候優先考慮是否有純數據接口。

前些年Web數據傳輸格式更多的可能是XML (eXtensible Markup Language),但是現在JSON(Javascript Object Notation) 已成爲Web數據傳輸的首選,因爲JSON相比XML容易閱讀、解析更快、佔用空間更少、對前端友好。

 

而且純JSON數據相對於網頁來說解析更加簡單,從json開始講起。

2.網頁解析

除了純JSON數據之外,更多的是返回網頁,所以網頁解析是一個重要的知識點。

網頁解析的庫非常多,但是常用的也就那幾個,所以豬哥就重點講幾個吧:

  1. 正則:正則匹配網頁內容,但是效率低,侷限性大。

  2. beautifulsoup4:美味湯,簡單易於上手,很多人學的第一個解析庫。

  3. lxml:XPath標準的實現庫,據說解析速度很快。

  4. pyquery:聽名字就知道語法和jquery相似,對熟悉jquery的同學會是個不錯的選擇。

  5. requests_html:有些同學可能還沒聽過,這是2018年新出的一個解析庫,是requests庫作者開發的,很多人相信它會成爲主流解析庫。

常用解析庫大概就這幾個,如果你覺得還有其他的好用解析庫也歡留言!

五 33個爬蟲項目實戰

WechatSogou [1]- 微信公衆號爬蟲。基於搜狗微信搜索的微信公衆號爬蟲接口,可以擴展成基於搜狗搜索的爬蟲,返回結果是列表,每一項均是公衆號具體信息字典。

DouBanSpider [2]- 豆瓣讀書爬蟲。可以爬下豆瓣讀書標籤下的所有圖書,按評分排名依次存儲,存儲到Excel中,可方便大家篩選蒐羅,比如篩選評價人數>1000的高分書籍;可依據不同的主題存儲到Excel不同的Sheet ,採用User Agent僞裝爲瀏覽器進行爬取,並加入隨機延時來更好的模仿瀏覽器行爲,避免爬蟲被封。

zhihu_spider [3]- 知乎爬蟲。此項目的功能是爬取知乎用戶信息以及人際拓撲關係,爬蟲框架使用scrapy,數據存儲使用mongo

bilibili-user [4]- Bilibili用戶爬蟲。總數據數:20119918,抓取字段:用戶id,暱稱,性別,頭像,等級,經驗值,粉絲數,生日,地址,註冊時間,簽名,等級與經驗值等。抓取之後生成B站用戶數據報告。

SinaSpider [5]- 新浪微博爬蟲。主要爬取新浪微博用戶的個人信息、微博信息、粉絲和關注。代碼獲取新浪微博Cookie進行登錄,可通過多賬號登錄來防止新浪的反扒。主要使用 scrapy 爬蟲框架。

distribute_crawler [6]- 小說下載分佈式爬蟲。使用scrapy,Redis, MongoDB,graphite實現的一個分佈式網絡爬蟲,底層存儲MongoDB集羣,分佈式使用Redis實現,爬蟲狀態顯示使用graphite實現,主要針對一個小說站點。

CnkiSpider [7]- 中國知網爬蟲。設置檢索條件後,執行src/CnkiSpider.py抓取數據,抓取數據存儲在/data目錄下,每個數據文件的第一行爲字段名稱。

LianJiaSpider [8]- 鏈家網爬蟲。爬取北京地區鏈家歷年二手房成交記錄。涵蓋鏈家爬蟲一文的全部代碼,包括鏈家模擬登錄代碼。

scrapy_jingdong [9]- 京東爬蟲。基於scrapy的京東網站爬蟲,保存格式爲csv。

QQ-Groups-Spider [10]- QQ 羣爬蟲。批量抓取 QQ 羣信息,包括羣名稱、羣號、羣人數、羣主、羣簡介等內容,最終生成 XLS(X) / CSV 結果文件。

wooyun_public[11]-烏雲爬蟲。 烏雲公開漏洞、知識庫爬蟲和搜索。全部公開漏洞的列表和每個漏洞的文本內容存在MongoDB中,大概約2G內容;如果整站爬全部文本和圖片作爲離線查詢,大概需要10G空間、2小時(10M電信帶寬);爬取全部知識庫,總共約500M空間。漏洞搜索使用了Flask作爲web server,bootstrap作爲前端。

spider[12]- hao123網站爬蟲。以hao123爲入口頁面,滾動爬取外鏈,收集網址,並記錄網址上的內鏈和外鏈數目,記錄title等信息,windows7 32位上測試,目前每24個小時,可收集數據爲10萬左右

findtrip [13]- 機票爬蟲(去哪兒和攜程網)。Findtrip是一個基於Scrapy的機票爬蟲,目前整合了國內兩大機票網站(去哪兒 + 攜程)。

163spider [14] - 基於requests、MySQLdb、torndb的網易客戶端內容爬蟲

doubanspiders[15]- 豆瓣電影、書籍、小組、相冊、東西等爬蟲集 writen by Python

QQSpider [16]- QQ空間爬蟲,包括日誌、說說、個人信息等,一天可抓取 400 萬條數據。

baidu-music-spider [17]- 百度mp3全站爬蟲,使用redis支持斷點續傳。

tbcrawler[18]- 淘寶和天貓的爬蟲,可以根據搜索關鍵詞,物品id來抓去頁面的信息,數據存儲在mongodb。

stockholm [19]- 一個股票數據(滬深)爬蟲和選股策略測試框架。根據選定的日期範圍抓取所有滬深兩市股票的行情數據。支持使用表達式定義選股策略。支持多線程處理。保存數據到JSON文件、CSV文件。

BaiduyunSpider[20]-百度雲盤爬蟲。

Spider[21]-社交數據爬蟲。支持微博,知乎,豆瓣。

proxy pool[22]-Python爬蟲代理IP池(proxy pool)。

music-163[23]-爬取網易雲音樂所有歌曲的評論。

jandan_spider[24]-爬取煎蛋妹紙圖片。

CnblogsSpider[25]-cnblogs列表頁爬蟲。

spider_smooc[26]-爬取慕課網視頻。

CnkiSpider[27]-中國知網爬蟲。

knowsecSpider2[28]-知道創宇爬蟲題目。

aiss-spider[29]-愛絲APP圖片爬蟲。

SinaSpider[30]-動態IP解決新浪的反爬蟲機制,快速抓取內容。

csdn-spider[31]-爬取CSDN上的博客文章。

ProxySpider[32]-爬取西刺上的代理IP,並驗證代理可用性

2018.8.2更新:

webspider[33]-本系統是一個主要使用python3, celery和requests來爬取職位數據的爬蟲,實現了定時任務,出錯重試,日誌記錄,自動更改Cookies等的功能,並使用ECharts + Bootstrap 來構建前端頁面,來展示爬取到的數據。

六 總結

1 該文章主要講解python爬蟲內容,代碼分析,借鑑了其它很好的python帖子。

2 python 和爬蟲的學習要不斷嘗試,逐步進步。

3 python 和爬蟲的學習也應該多理解其中的原理,爲己所用。

4 該帖子有很多不完善的地方歡迎留言。

 參考資料

  1. https://www.jb51.net/article/164829.htm
  2. python爬蟲項目設置一箇中斷重連的程序的實現
  3. 詳解python3 + Scrapy爬蟲學習之創建項目
  4. Python即時網絡爬蟲項目啓動說明詳解
  5. Python網絡爬蟲項目:內容提取器的定義
  6. python小項目之五子棋遊戲
  7. Python 項目轉化爲so文件實例
  8. 解決python web項目意外關閉,但佔用端口的問題
  9. python+Django+pycharm+mysql 搭建首個web項目詳解
  10. 三個python爬蟲項目實例代碼
  11. https://blog.csdn.net/u014044812/article/details/78894108
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章