目錄
一 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數據之外,更多的是返回網頁,所以網頁解析是一個重要的知識點。
網頁解析的庫非常多,但是常用的也就那幾個,所以豬哥就重點講幾個吧:
-
正則:正則匹配網頁內容,但是效率低,侷限性大。
-
beautifulsoup4:美味湯,簡單易於上手,很多人學的第一個解析庫。
-
lxml:XPath標準的實現庫,據說解析速度很快。
-
pyquery:聽名字就知道語法和jquery相似,對熟悉jquery的同學會是個不錯的選擇。
-
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 該帖子有很多不完善的地方歡迎留言。
參考資料
- https://www.jb51.net/article/164829.htm
- python爬蟲項目設置一箇中斷重連的程序的實現
- 詳解python3 + Scrapy爬蟲學習之創建項目
- Python即時網絡爬蟲項目啓動說明詳解
- Python網絡爬蟲項目:內容提取器的定義
- python小項目之五子棋遊戲
- Python 項目轉化爲so文件實例
- 解決python web項目意外關閉,但佔用端口的問題
- python+Django+pycharm+mysql 搭建首個web項目詳解
- 三個python爬蟲項目實例代碼
- https://blog.csdn.net/u014044812/article/details/78894108