爬蟲 - Web Spider
定義: 爬蟲/Spider/信息采集
流程: 獲取網(wǎng)頁
分析提取
保存信息
自動化程序
HTTP基本原理
URI和URL
URI(Uniform Resource Identifier)統(tǒng)一資源標志符
URL(Universal Resource Locator)統(tǒng)一資源定位符
URL是URI的子集,URI還包括一個子類URN(Universal Resource Name)統(tǒng)一資源名稱,URN只命名資源不指定如何定位資源
超文本(hypertext)
網(wǎng)頁源代碼,html代碼
查看源代碼工具和方法
HTTP和HTTPS
HTTP(Hyper Text Transfer Protocol)
超文本傳輸協(xié)議
http與https的區(qū)別
Https是網(wǎng)絡安全的協(xié)議,http是不安全的協(xié)議
Https默認端口是443,http是80
https是建立在安全套階層(ssl)基礎(chǔ)之上,所以是可靠的
安裝https需要證書,分公鑰和私鑰,申請證書需要專門的機構(gòu)才能下發(fā)
當然購買的話可以在阿里云上購買,一年大概2000以上,每天都要付費
系統(tǒng)的性能排查
普通單個頁面打開需要在2秒之內(nèi),首頁一般需要控制在1秒左右
網(wǎng)絡速度是否正常 ping,檢查下路由的路徑
網(wǎng)絡資源加載速度
服務器和服務器之間(比如說django服務器和數(shù)據(jù)庫服務器之間的通信速度和帶寬)
Put方法和Post方法有什么區(qū)別?
Post是新增操作
Put是修改操作
同樣的表單信息,用post提交和用put提交哪一個會有變化? --- Post
mbp是什么? - macbook pro
為什么會有cookie,cookie跟session有什么區(qū)別?
http本身是無狀態(tài)的,用cookie可以在不同的請求之間交換一個狀態(tài),比如說
用戶id信息,達到識別用戶的目的。cookie里面不要放太多信息和敏感信息。
Cookie是保存在客戶端的,session是保存在服務端的。
比如說:django項目里面mysql數(shù)據(jù)庫里有一個django_session表,這個表里面有一個session_id字段和value字段。這個session_id的值是同時保存在客戶端cookie里面.然后每次請求的時候,通過發(fā)送這個session_id到服務端,服務端通過查找對應session_id的value找到session里面具體存放的東西。


你們的web服務器對500錯誤是如何處理的?
500錯誤是內(nèi)部服務器錯誤,我們會做一個統(tǒng)一錯誤捕獲和處理的中間件,然后我們一般顯示給用戶說‘系統(tǒng)繁忙,請稍后再試’,千萬django配置里面不能把debug設(shè)為true
普通爬蟲的開發(fā)步驟
分為 獲取網(wǎng)頁、分析網(wǎng)頁、儲存數(shù)據(jù)、自動化 4個步驟
你用的那個包獲取的網(wǎng)頁
requests 獲取的網(wǎng)頁,requests.get()方法返回一個響應對象
response.concent返回的是什么?
返回的是字節(jié)流(二進制)轉(zhuǎn)換成字符串需要用response.content.decode('utf-8')
解析網(wǎng)頁的方法主要有三種:re、xpath、css
在學習爬蟲的過程中,要總結(jié)你做的爬蟲的所有項目(用excel記錄)
你爬的是哪個網(wǎng)站,這個網(wǎng)站做什么的(貓眼,買電影票的)
你爬取的數(shù)據(jù)量有多大(總共10頁,每頁10條,共100條數(shù)據(jù))
你爬取的頻率(每天自動爬取一次(todo))
遇到的反爬有什么,如何解決的(user-agent)
還遇到了什么問題,如何解決的
補充知識:
關(guān)系型數(shù)據(jù)庫
開源
MySql,postgresql
企業(yè)
Sql server - 微軟
oracle - 甲骨文
db2
sybase
Windows Server 2012/2008
開源數(shù)據(jù)庫
Mysql community enterprise
mysql -u root -p
mysql存儲引擎
Mylsam 適合與查詢,不支持事務
lnnodb 支持事務(mysql默認)
事務:多個操作要么同時完成,要么都不完成
我們用requests獲取的網(wǎng)頁是從服務端直接拉下來的,沒有經(jīng)過瀏覽器的前端渲染,就是js并沒有運行,所以你在開發(fā)者工具的頁面元素查看中看到的東西不一定有,需要到開發(fā)者工具的網(wǎng)絡欄里面去看實際的頁面長什么樣子。如果實際頁面里沒有內(nèi)容,那一般都是通過ajax調(diào)用動態(tài)加載的數(shù)據(jù),然后用js渲染到頁面上的。ajax的請求我們一般通過xhr和js兩個欄目來定位
請你描述一下MTV或者MVC模式
M - Model 模型
T - Template 模板 做一個展現(xiàn)(可以展現(xiàn)在手機、pc、大屏都可以,做自適應bootstrap)
V - View 視圖 通過存取model的數(shù)據(jù)然后再把數(shù)據(jù)丟給template,view里面可以寫跟數(shù)據(jù)和響應數(shù)據(jù)有關(guān)的邏輯。如果代碼太多,我們需要新建helper或util進行代碼封裝
model 是拿來做什么的,沒有行不行?
model是用來跟數(shù)據(jù)庫進行交互的,做一個數(shù)據(jù)的存取。沒有他其實是可以的,我們可以用pymysql也可以做,但是為什么我們還要他呢?
我們用model來做一個面向?qū)ο蟮牟僮?,為不是sql語句
model幫我們隔離了具體的數(shù)據(jù)庫實現(xiàn)(比如說剛開始我們用的是mysql數(shù)據(jù)庫,等我們上線了后,客戶提出換成oracle數(shù)據(jù)庫,如果我們是直接用sql寫的,那我們就需要改成百上千的代碼,而且還不知道哪些需要改) ,如果用的是model,我們只需要在settings里面改一下數(shù)據(jù)庫驅(qū)動和連接就行了。
MySQL知識補充
truncate table 表名; -- 清空表數(shù)據(jù)(消除自增)
delete from 表名; -- 清空表數(shù)據(jù)(不消除自增)
show create table 表名; - 查看表結(jié)構(gòu)的真實情況
小TIPS
如果你放進去的是垃圾,那么你后面肯定就會拿到垃圾
弱水三千,只取一瓢
https://blog.csdn.net/sinat_37064286/article/details/82224562
win創(chuàng)建虛擬環(huán)境,在當前目錄下:python -m venv venv
激活:venv/Scripts/activate
linux激活:source venv/Scripts/activate
你有沒有遇到過數(shù)據(jù)量很大,查詢很慢的時候?
遇到過,我們之前有一個項目他的數(shù)據(jù)量達到了千萬級,剛開始數(shù)據(jù)量小的時候,性能還不錯,能在3秒之內(nèi)。后面上線了半年之后,我們數(shù)據(jù)量上去了,查詢就變得很慢,比如說1分鐘才出來
你是如何解決的呢?
我給相應的字段添加了索引,變快了!
是真的嗎?如何加的?
直接加的!
你可以走了
解釋:
當數(shù)據(jù)量很大的時候,而且系統(tǒng)還在運行時,你加索引會卡死,會造成一定的數(shù)據(jù)庫表死鎖,然后系統(tǒng)就會報500錯誤。
解決方法:
先創(chuàng)建一張空表和原來的那個表結(jié)構(gòu)一模一樣,在創(chuàng)建表的時候就把這張新表的索引建立好
再把舊表里面的數(shù)據(jù)導入到新表里面
把舊表集名字改成另外的名字,把新表名改稱舊表的名字
索引就加上了
安裝Selenium
1.查看chrome版本

2.下載驅(qū)動
http://npm.taobao.org/mirrors/chromedriver
下載對應版本,之后解壓

在D:根目錄建立一個tools空目錄
將chromedriver.exe復制進去

然后添加環(huán)境變量

直接在系統(tǒng)變量中的path編輯新建,然后移到首位!
在cmd中打出:chromedriver.exe

如果看到以下圖片說明成功

最后直接終端輸入:pip install selenium
隱式等待和顯示等待得區(qū)別
顯示等待 - 就是你指定等待多久,他就會固定等待那么久,即使元素加載完畢他也會等到固定得時間才完成,比如說time.sleep(5)這就是顯示等待
隱式等待 - 就是你指定一個等待時間,如果在這個等待時間之內(nèi)完成,那就不用一直等到固定的時間結(jié)束,會馬上完成。如果過了這個指定的等待時間都沒完成,會拋出異常
代理IP
可以使用的范圍:爬蟲采集,秒殺搶購,營銷補量(刷流量),投票助力
urllib,requests,selenium 都可以設(shè)置IP
IP代理池主要包含哪些模塊
爬取模塊 - 負責從提供代理IP的網(wǎng)站把ip和端口收集回來,有很多網(wǎng)站(比如西刺)
存儲模塊 - 我們采用redis作為代理ip保存的數(shù)據(jù)庫,我們使用redis里面的有序集合來保存
測試模塊 - 用我們要爬取的目標網(wǎng)址去定時測試數(shù)據(jù)庫里面的ip是否有效,并進行打分或者去除
接口模塊 - 通過讀取數(shù)據(jù)庫里面的高分的代理IP,提供給使用方(我們使用的是flask或者Django)
你是計算機專業(yè)的?
學過操作系統(tǒng)沒有?
知道操作系統(tǒng)主要負責那些東西?
磁盤管理,進程管理,內(nèi)存管理,輸入輸出管理
上周回顧
爬蟲得大體介紹(爬蟲能做什么,如何學)
http協(xié)議的介紹(請求頭,請求體,響應頭,響應體,http method, http status code )
獲取網(wǎng)頁的方法(urllib,requests,seleinum)
解析網(wǎng)頁得方法(re,xpath-lxml,css-bs4)
獲取動態(tài)網(wǎng)頁的方法(ajax請求,selenium)
保存解析后得內(nèi)容(pymysql,sqlalchemy)
用selenium實現(xiàn)網(wǎng)頁操作自動化(get element,click, input, drag and drop,iframe,action chains...)
驗證碼,圖形驗證碼(超級鷹),滑塊驗證碼(極驗驗證碼),短信驗證碼,旋轉(zhuǎn)驗證碼
ip代理池 企查貓故意讓他屏蔽ip,使用了付費代理ip(蘑菇代理)
爬蟲 scrapy框架
特點:爬取效率高,擴展性強,python編寫跨平臺運行
數(shù)據(jù)流程


mongodb數(shù)據(jù)庫
NoSQL,全稱是"Not Only Sql",指的是非關(guān)系型的數(shù)據(jù)庫。
非關(guān)系型數(shù)據(jù)庫主要有這些特點:非關(guān)系型的、分布式的、開源的、水平可擴展的。
水平擴展和垂直擴展的區(qū)別
水平擴展:通過增加服務器數(shù)量的方式來進行擴展,對每臺服務器的要求不高,可以靈活的增加或減少服務器 - - 我們現(xiàn)在一般都使用水平擴展
垂直擴展:通過增加服務器的硬件性能來進行擴展,對服務器的要求很高,不是太靈活然后更換費用貴
NoSQL的擁護者們提倡運用非關(guān)系型的數(shù)據(jù)存儲,通常的應用如:模式自由、支持簡易復制、簡單的AP1、大容量數(shù)據(jù)等。
MongoDB是一個介于關(guān)系數(shù)據(jù)庫和非關(guān)系數(shù)據(jù)庫之間的產(chǎn)品,是非關(guān)系數(shù)據(jù)庫當中功能最豐富,最像關(guān)系數(shù)據(jù)庫的。
MongoDB最大的特點:它支持的查詢語言非常強大,其語法有點類似于面向?qū)ο蟮牟樵冋Z言,幾乎可以實現(xiàn)類似關(guān)系數(shù)據(jù)庫單表查詢的絕大部分功能。它是一個面向集合的,模式自由的文檔型數(shù)據(jù)庫。
1、面向集合(Collection-Orented)……意思是數(shù)據(jù)被分組存儲在數(shù)據(jù)集中,被稱為一個集合(Collection),每個集合在數(shù)據(jù)庫中 都有有一個唯一的標識名,并且可以包含無限數(shù)目的文檔。集合的概念類似關(guān)系型數(shù)據(jù)庫(RDBMS)里的表(table),不同的是它不需要定義任何模式
2、模式自由(schema-free)存儲在MongoDB數(shù)據(jù)庫中的文件,我們不需要知道它的任何結(jié)構(gòu)定義。提了這么多次"無模式"或"模式自由",它到是個什么概念呢?例如,下面兩個記錄可以存在于同一個集合里面
3、文檔型意思是我們存儲的數(shù)據(jù)是鍵-值對的集合,鍵是字符串,值可以是數(shù)據(jù)類型集合里的任意類型,包括數(shù)組和文檔,文件存儲格式為BSON(一種JSON的擴展)
適用場景
1網(wǎng)站數(shù)據(jù):MongoDB非常適合實時的插入,更新與查詢,并具備網(wǎng)站實時數(shù)據(jù)存儲所需的復制及高度伸縮性
2緩存:由于性能很高,MongoDB也適合作為信息基礎(chǔ)設(shè)施的緩存層。在系統(tǒng)重啟之后,由MongoDB搭建的持久化緩存層可以避免下層的數(shù)據(jù)源過載
3大尺寸,低價值的數(shù)據(jù):使用傳統(tǒng)的關(guān)系型數(shù)據(jù)庫存儲一些數(shù)據(jù)時可能會比較昂貴,在此之前,很多時候程序員往往會選擇傳統(tǒng)的文件進行存儲
4高伸縮性的場景:MongoDB非常適合由數(shù)十或數(shù)百臺服務器組成的數(shù)據(jù)庫。MongoDB的路線圖中已經(jīng)包對MapReduce引擎的內(nèi)置支持
5用于對象及JSON數(shù)據(jù)的存儲:MongoDB的BSON數(shù)據(jù)格式非常適合文檔化格式的存儲及查詢
服務端命令
bin/mongod.exe
客戶端命令
mongo.exe
MongoDB簡單使用
use 數(shù)據(jù)庫名字 - 建立數(shù)據(jù)庫(沒有就建立,有就切換)
如果是新建,這個時候還沒有數(shù)據(jù),但我們給數(shù)據(jù)庫中的集合插入文檔的時候自動創(chuàng)建文檔,集合,數(shù)據(jù)庫
db.集合名.insert({'字段1':值1,'字段2':值2,......}) - 插入數(shù)據(jù),值也可以是字典集合任何東西
db.集合.find() - 查詢集合內(nèi)所有數(shù)據(jù)
db.集合.find({'name':'張三'}) - 查詢集合內(nèi)'name'叫'張三'的數(shù)據(jù)
db.集合.remove({'name':'張三'}) - 刪除叫'張三'的數(shù)據(jù)
db.集合.remove({}) - 清空集合
de.集合.drop() - 刪除集合
db.dropDatabase() - 刪庫
db.集合.update({'原字段':'原值'},{'新字段':'新值'}) - 第一個文檔為查詢文檔,第二個為更新后的文檔,更新之后的文檔會將整個之前的原文檔全部覆蓋
使用修改器inc':{'price':100}}) - 給集合中對應的文檔中price增加100


安裝scrapy
windows
第一步
檢查自己的python版本

虛擬環(huán)境和全局不一樣,根據(jù)自己的需求選擇
第二步
打開網(wǎng)址
https://www.lfd.uci.edu/~gohlke/pythonlibs/
下載Twisted
找到跟自己python對應的版本

下載完成后放到一個比較容易找到的目錄,路勁中不要出現(xiàn)中文
然后安裝:
打開你的終端,虛擬環(huán)境或全局根據(jù)剛才查詢Python版本一樣
pip install D:\tools\Twisted-19.10.0-cp37-cp37m-win_amd64.whl

后面跟你的剛才文件的絕對路徑
如果怕把路徑打錯可以直接cd到對應的目錄下直接跟文件名
第三步
接下來繼續(xù)安裝pypiwin32和scrapy
pip install pypiwin32
pip install scrapy

如果都成功了就大功告成!
使用Scrapy
以下命令都直接再終端輸入,不要運行程序
項目結(jié)構(gòu)


scrapy crawl 蜘蛛 -o result.json
前端補充:
取a標簽里面的href屬性
attr是連接,text是文字
response.selector.css('.author.box a::類型(屬性)').extract()
response.selector.css('.author.box a::attr(href)').extract()
linux補充
cp -av 地址 - 拷貝有內(nèi)容的目錄
scp 文件名 root@主機ip:/root/目錄 - 在本地遠程給linux傳輸文件
python3 -m venv venv - 建立虛擬環(huán)境
source venv/bin/activate - 激活虛擬環(huán)境
deactivate - 退出虛擬環(huán)境
網(wǎng)頁簡單部署上線!
1.在本地先建立自己的django項目保證能正常運行!
2.在阿里云建立相同的工程目錄
3.在工程目錄下建立虛擬環(huán)境
python3 -m venv venv
看見venv文件夾之后激活虛擬環(huán)境
source venv/bin/activate
4.到本地的django工程生成最新的依賴項清單
終端輸入
pip freeze > requirements.txt
5.然后找到這個requirements.txt的目錄下,打開終端
將文件傳送到服務器
scp 文件名 root@主機ip:/root/目錄
拷過去之后pip install -r requirements.txt

全部搞定之后到本地打包整個django項目文件
直接打包成zip格式最好
然后一樣上傳到服務器剛才那個目錄下
然后解壓
unzip 文件名
如果沒有安裝unzip直接安裝:yum install unzip 就行
解壓成功之后可以直接python manage.py runserver 0.0.0.0:8080
如果沒報錯就可以直接訪問了
前提必須要把相應的端口打開
確認沒問題之后將setting的debug改為False
下面這條代碼可以后臺運行
nohup python manage.py runserver 0.0.0.0:8080 &
注意:
如果改為debug false之后無法加載靜態(tài)資源可以
manage.py runserver --insecure
這條命令可以在不安全的模式下運行
上傳到服務器記得將數(shù)據(jù)庫配置跟服務器一致,
如果使用pymysql啟動數(shù)據(jù)庫那django的版本不能太高2.1.4就行
如果要使用高版本的django就只能用mysql-client
關(guān)閉項目直接殺死進程就行
kill -9 進程號
scrapy本身是不帶分布式功能的,裝了插件之后才實現(xiàn)了分布式功能
Scrapy去重原理(考點)
在你構(gòu)建請求的時候,傳一個dont_filter=False參數(shù)可以開啟去重,默認是沒有開啟的,
在scrapy源碼中可以找到一個dupefilters.py去重器;這個里面有去重的邏輯。它會把傳過去的request對象通過hash摘要的形式生成16進制的hash碼。然后判斷是否重復就是比較這個hash碼,存放request指紋是使用python的set類型。
梅開二度(駱神版)
獲取網(wǎng)頁源代碼
resp = requests.get(url)
resp.content / resp.text
解析網(wǎng)頁源代碼
正則表達式解析-re
xpath解析 - lxml
css選擇器 - pyquery / beautifulsoup
document.querySelector('#hello')
document.querySelectorAll('.good')
比較靠譜的商業(yè)ip代理

列表雙關(guān)隊列
deque,跟列表差不多,效率極高
from collection import deque
a =deque()
添加 - append
左邊添加 - appendleft
最后刪除 - pop
左邊刪除 - popleft
自動拼接url
urljoin(url1,url2)
自動解析url
urlparse(url)
代理池
cookie池
線程池也可以用上下文語法
迭代器協(xié)議,iter,next
上下文協(xié)議,enter,exit
多線程爬蟲+多線程寫入數(shù)據(jù)
示例:
import hashlib
import io
from collections import deque
from concurrent.futures.thread import ThreadPoolExecutor
from threading import local
from urllib.parse import urljoin, urlparse
import MySQLdb
import bs4
import requests
MAX_DEPTH = 3
visited_urls = set()
new_urls = deque()
seed_url = 'https://sports.sohu.com/'
netloc = urlparse(seed_url).netloc
def _force_bytes(data):
if type(data) != bytes:
if type(data) == str:
data = data.encode()
elif type(data) == io.BytesIO:
data = data.getvalue()
else:
data = bytes(data)
return data
def make_md5_digest(data):
"""生成MD5摘要"""
data = _force_bytes(data)
return hashlib.md5(data).hexdigest()
def get_db_connection():
thread_local = local()
if not hasattr(thread_local, 'conn'):
conn = MySQLdb.connect(host='主機', port=3306,
user='用戶名', password='用戶密碼',
database='數(shù)據(jù)庫', charset='編碼',
autocommit=True)
setattr(thread_local, 'conn', conn)
return getattr(thread_local, 'conn')
def write_to_db(url, content, digest):
with get_db_connection().cursor() as cursor:
cursor.execute(
'insert into tb_page (url, content, digest) values (%s, %s, %s)',
(url, content, digest)
)
def fix_url(curr_url, href):
if href.startswith('//'):
href = f'http:{href}'
elif href.startswith('/'):
href = urljoin(curr_url, href)
elif href.startswith('mailto') or href.startswith('javascript')\
or href.startswith('#') or href.startswith('?'):
href = ''
href = href.replace('!\'\'}', '')
return urljoin('http://', href) if href else href
def fetch_page(curr_url, depth):
try:
resp = requests.get(curr_url, timeout=1)
except Exception as ex:
print(ex)
else:
write_to_db(curr_url, resp.text, make_md5_digest(resp.text))
soup = bs4.BeautifulSoup(resp.text, 'lxml')
if depth < MAX_DEPTH:
all_anchors = soup.find_all('a')
for anchor in all_anchors:
href = anchor.attrs.get('href', '')
href = fix_url(curr_url, href)
if urlparse(href).netloc == netloc:
new_urls.append((href, depth + 1))
def main():
new_urls.append((seed_url, 0))
with ThreadPoolExecutor(max_workers=32) as pool:
future = None
while len(new_urls) > 0 or (future and not future.done()):
# print(len(new_urls))
if len(new_urls) > 0:
curr_url, depth = new_urls.popleft()
if curr_url not in visited_urls:
visited_urls.add(curr_url)
future = pool.submit(fetch_page, curr_url, depth)
if __name__ == '__main__':
main()