前言
本篇為技術篇,,會講解各種爬蟲庫的使用,至於庫的安裝在安裝篇已經介紹了
requests出現中文亂碼
這種情況是網頁沒有設置編碼,獲取不到,所以使用了默認的編碼,這個時候中文就會出現亂碼的情況
只需要多加一行代碼即可
response.encoding='gb2312'
使用代理
免費的代理我試着不行,暫時不研究了,寫一下付費的代理是怎么使用的
我購買的是訊代理,購買之后先在白名單里面添加自己的IP,然后點擊生成API
選擇訂單,選擇一個城市,然后生成json
接下來使用Python獲取代理IP,我是獲取了之后存到數據庫了
import pyodbc
import json
import requests
import sys
import time
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
conn = pyodbc.connect('DRIVER={SQL Server};SERVER=192.168.3.8,1433;DATABASE=VaeDB;UID=sa;PWD=test123')
cursor = conn.cursor()
r = requests.get('http://apXXXXXXXXXXXXXXXXXXXXX40300', headers=headers)
print(r.text)
jsonobj = json.loads(str(r.text))
datetime=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
cursor.execute(
'update ProxyIP set IP=?,UpdateDate=? where Id=1', (jsonobj['RESULT'][0]['ip']+':'+jsonobj['RESULT'][0]['port'],datetime))
conn.commit()
sys.exit()
怎么使用代理,這個分為好幾個情況,我使用的是Selenium,就寫這個,requests的用到再補充
conn = pyodbc.connect(
'DRIVER={SQL Server};SERVER=111.108.8.2,1433;DATABASE=VaeDB;UID=sa;PWD=testxxxx')
cursor = conn.cursor()
cursor.execute("""
select IP from dbo.ProxyIP
"""
)
data = cursor.fetchone()
proxyip=str(data[0])
chrome_options=webdriver.ChromeOptions()
chrome_options.add_argument('--proxy-server=http://'+ proxyip)
browser = webdriver.Chrome(chrome_options=chrome_options)
BeautifulSoup的使用
我使用BeautifulSoup爬取了好幾萬的數據了,對於普通的網站,BeautifulSoup真的很好用
#BeautifulSoup初始化可以自動更正HTML格式,補全沒有閉合的元素
print (soup.prettify())#以標准的縮進格式輸出
print(soup.title)#標題
print(soup.title.string)#標題里面的內容
print(soup.title.name)#title的節點名稱,就是title
print(soup.p)#第一個p元素的內容
print(soup.p.attrs)#第一個p元素的所有屬性和值
print(soup.p['class'])#第一個p元素class屬性的值
print(soup.p['name'])#第一個p元素name屬性的值
print(soup.p.b.string)#第一個p標簽下的b標簽的文本內容
print(soup.p.contents)#第一個p元素下的所有子節點,不包括孫子節點
#第一個p元素所有的子節點
print(soup.p.descendants)
for i,child in enumerate(soup.p.descendants):
print(i,child)
print(soup.p.parent)#第一個p元素的父節點
#第一個p元素所有的父節點
print(soup.p.parents)
print(list(enumerate(soup.p.parents)))
print(soup.p.next_sibling)#第一個p元素的下一個兄弟節點,注意有回車的時候要寫兩個next_sibling
print(list(enumerate(soup.p.next_siblings)))#第一個p元素后面的所有兄弟節點
print(soup.p.previous_sibling)#第一個p元素的上一個兄弟節點
print(list(enumerate(soup.p.previous_siblings)))#第一個p元素前面的所有兄弟節點
#########################################################
#下面這些是比較常用的,上面的了解一下即可
# 判斷某個標簽是否有屬性,例如img標簽有一個alt屬性,有時候img沒有alt屬性,我就可以判斷一下,否則出錯
if img.attrs.get('alt'):
soup.find(id='text-7').find_all(name='li')
#根據文本查找到該標簽
# 例如下面的,根據Description查找含有Description的第一個p元素
test = soup.find(lambda e: e.name == 'p' and 'Description' in e.text)
# 其實如果是直接子元素的話,也可以使用parent,但是這個很少用,適用情況不多
test= soup.find(text=re.compile('Description')).parent
#查找某個屬性為包含的標簽
#標簽的屬性有很多值,例如img標簽的alt屬性,有item和img兩個值,可以通過如下查找
noscript.find_all('img',attrs={'alt':re.compile('item')})
#判斷屬性里面是否有某個值
if 'Datasheet' in img['alt']:
#替換所有的br換行符號
html = get_one_page(url)
return html.replace('<br>', '').replace('<br />', '').replace('<br/>', '')
#去除最后一個逗號
datasheet_url.rstrip(',')
#去除關鍵字和空格,只要后面的內容
#例如 Function : Sensitive Gate Silicon Controlled Rectifiers
#得到的就是Sensitive Gate Silicon Controlled Rectifiers
return re.sub(keywords+'.*?[\s]:.*?[\s]', '', child.find(text=re.compile(keywords)).string)
#返回某個符號之前的字符
import re
text="K6X4008C1F-BF55 ( 32-SOP, 55ns, LL )"
b=re.search('^[^\(]*(?=\()',text,re.M)
if b:
print(b.group(0))
print(len(b.group(0)))
else:
print('沒有')
#關鍵地方是,這里是匹配的( 括號需要\來轉義一下
^[^\(]*(?=\()
#如果是逗號,可以寫
^[^,]*(?=,)
#如果是單詞,比如我想匹配Vae這個單詞,如下
text='XuSong Vae hahaha'
text2='VV Vae hahaha'
b=re.search('^[^Vae]*(?=Vae)',text,re.M)
#這個例子很重要,text是可以正則出XuSong的,但是下面的VV就正則不出來了,因為^是后面的Vae的任意一個單詞,只要前面包含就不行,VV包含了V,所以就不行了,我嘗試着給Vae加括號,也不行.然后我就想了一個辦法,把Vae替換成逗號之類的符號不就可以了,只要是一個字符就行,如下
text='XuSong Vae hahaha'
text2='VV Vae hahaha'
b=re.search('^[^,]*(?=,)',text.replace('Vae',','),re.M)
#一段HTML元素中去除a標簽,但是保留a標簽的值
return re.sub('(<\/?a.*?>)', '', description_element)
#有時候想獲取一段HTML元素內容,因為有的排版在,比如ul和li元素,排版是在的,如果使用text就是一串文本,換行都沒了,可以這樣
str(child.find(class_='ul2')) #獲取到這段HTML元素之后,使用str函數變成字符串即可
#下一個兄弟元素最好使用find_next_sibling()
#等待驗證,和next_sibling比較一下再說
驗證完畢,我來講一下find_next_sibling()和next_sibling的區別
如果想要后面的元素的話,寫find_next_sibling()
如果想要后面的內容的話,寫next_sibling
例如
<a>111</a><p>222</p> 想要獲取p元素應該使用find_next_sibling()
<a>111</a>我是文本 想要獲取 我是文本 應該使用next_sibling
#Python爬蟲數據插入到MongoDB
import pymongo
client = pymongo.MongoClient("mongodb://admin:test123@192.168.3.80:27017/")
db = client.datasheetcafe
collection = db.datasheetcafe
collection.insert_one(message)
Selenium的使用
在爬取一個新網站的時候,我發現網站上的網頁數據全都是動態加載的,瀏覽器加載之后數據才會顯示,這個時候BeautifulSoup就沒用了,完全獲取不到HTML節點
這種情況,可以使用Selenium進行動態加載,我使用這個已經爬取了近200萬的數據
基礎使用
#有不顯示瀏覽器的,但是我選擇了Chrome瀏覽器
browser = webdriver.Chrome()
#先獲取url,再選擇元素
browser.get(url)
div = browser.find_element_by_css_selector('.information')
#find_element_by_css_selector這個東西是css選擇器,如果選擇一個就使用這個,想要選擇一堆就加個s,使用find_elements_by_css_selector
#后面的類就是. id就是# 標簽名就直接寫,如下
table = browser.find_element_by_css_selector('#part-specs')
table.find_elements_by_css_selector('tr')
# 獲取屬性的值
tr.get_attribute('class')
#對了,下面這個獲取的可不是HTML元素,是一個WebElement元素
table = browser.find_element_by_css_selector('#part-specs')
#所以如果你想要HTML元素這樣寫
table = browser.find_element_by_css_selector('#part-specs').get_attribute('outerHTML')
#獲取里面的文本值就這樣寫
browser.find_element_by_css_selector('.part-number').text
#有一個a標簽,我使用a.get_attribute('href')獲取href屬性值是完全沒問題的,但是我獲取文本值a.text就獲取不到,原因應該是元素隱藏了,可以換一種方式
a.get_attribute('innerText') #獲取本文值
#至於怎么判斷元素是否存在的,我寫了if但是沒有,所以我利用try catch幫助解決
try:
button=browser.find_element_by_css_selector('#show-secondary-part-list')
button.click()
except Exception as e:
print('沒有這個元素')
#Selenium進行iframe切換
#網頁中有iframe的時候,是拿不到里面的HTML數據的,只能通過切換iframe
# 里面的可以是iframe的name或者id
browser.switch_to_frame('popup_frame')
# 再切換回主界面可以使用
browser.switch_to.default_content()
#selenium選擇下拉框
這個首先要引入一個Select的包
from selenium.webdriver.support.select import Select
然后執行的時候如此
Select(browser.find_element_by_css_selector ('.form-control')).select_by_value('all')
我是根據Value進行選擇的,也可以通過下標或者內容值
#通過name查找
<input name="username" type="text" />
username = driver.find_element_by_name('username')
# 通過超鏈接文本查找
<a href="continue.html">Continue</a>
continue_link = driver.find_element_by_link_text('Continue')
continue_link = driver.find_element_by_partial_link_text('Conti')
# 兄弟節點,如果是上一個元素或者父元素,對於selenium來說,使用XPath可能會方便很多
<div id="D"></div>
<div>brother 2</div>
driver.find_element_by_css_selector('div#D + div').text
# 找到直接子元素或者所有子孫元素
使用css的方法是,直接子元素:
browser.find_elements_by_css_selector('#ptp-overview > div')
所有子元素
browser.find_elements_by_css_selector('#ptp-overview div')
使用xpath的方法是,直接子元素
content.find_elements_by_xpath('div')
所有子元素
content.find_elements_by_xpath('//div')
Selenium執行js
有時候還是需要執行js的,特別是有的網頁僅僅執行一個js方法就可以獲取數據,太爽了
browser.execute_script('window.stop()') #同步方法
browser.execute_async_script('window.stop()') #異步方法
Selenium獲取網頁動態數據賦值給BeautifulSoup
單純的使用selenium進行爬蟲是真的不好用,雖然selenium有一些css選擇器,也可以使用XPath,但是還是不好用,還是BeautifulSoup使用起來順手,所以遇到那種必須瀏覽器加載才能出來數據的網站,可以使用Selenium獲取到整個網頁的源代碼數據,然后賦值給BeautifulSoup即可
soup = BeautifulSoup(browser.page_source, 'lxml')
就這么簡單,直接selenium的browser.page_source,給soup就ok了
Selenium加載時間過長
有時候Selenium加載一個網頁時間過長了,所以必須設定一個超時時間
browser = webdriver.Chrome()
browser.set_page_load_timeout(20)
browser.set_script_timeout(20)
try:
browser.get(address)
except TimeoutException:
browser.execute_script('window.stop()')
div = browser.find_element_by_css_selector('.information')
....
先設定加載時間為20秒,如果20都沒加載出來,就算了,直接停止加載,爬取網頁內容吧,如果一個HTML元素都沒抓取到,空白頁面,那就try catch跳過這個網頁
Selenium使用Chrome,隱藏Chrome
默認使用Chrome是加載出來的,也可以隱藏Chrome瀏覽器,那個無界面PhantomJS已經被淘汰了
chrome_options=webdriver.ChromeOptions()
# chrome_options.add_argument('--headless') 如果想不彈出chrome瀏覽器就開啟這兩行,那個PantomJS啥的已經過期了
# chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--proxy-server=http://'+ proxyip)
chrome_options.add_experimental_option('excludeSwitches', ['enable-automation']) #這一行是為了防止網站識別出我是selenium,參考:https://zhuanlan.zhihu.com/p/65077940
browser = webdriver.Chrome(chrome_options=chrome_options)
多進程下無法退出exe
我的爬蟲爬了一段時間就停止了,selenium控制的瀏覽器要么變成了空白頁,要么訪問失敗,最可怕的是頁面卡着不動了,這個時候只能重啟爬蟲程序了,所以我把爬蟲發布成exe了,我使用Windows自帶的計划任務,隔一段時間就啟動exe爬蟲,然后問題來了
我必須關閉exe的彈窗,不然加載越來越多的exe,內存會崩的,於是我采用了Python多進程
一個進程去爬蟲,一個進程去計時,如果計時10分鍾,就退出exe
但是沒有執行,無論我使用sys.exit()還是os._exit()都無法退出exe,至今不知道為什么
沒辦法只好采取了另外一種方法,殺進程
import os
os.system('taskkill /im conhost.exe /F')
# 這兩個不能同時執行,我寫了兩個py,發布兩個exe執行
os.system('taskkill /im chromedriver.exe /F')
os.system('taskkill /im chrome.exe /F')
我把控制台和Chrome全殺了.......這樣Windows計划任務會重啟exe,然后過10分鍾我再全殺了.....
對了,Win10的計划任務也搞了我好久,就是不成功,詳情見Windows計划任務
scrapy
暫留......
scrapy startproject tutorial
scrapy genspider quotes quotes.toscrape.com
scrapy crawl quotes
爬蟲小Demo
爬蟲小Demo應該不會再更新了,我爬取的東西不可能寫出來了,僅僅介紹技術,授人以漁吧
爬取知乎發現頁面的今日最熱
import requests
import re
headers = {'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
r =requests.get("https://www.zhihu.com/explore", headers=headers)
pattern = re.compile('explore-feed.*?question_link.*?>(.*?)</a>', re.S)
titles = re.findall(pattern,r.text)
print(titles)
講解:headers里面有瀏覽器標識,不加這個知乎會禁止抓取
爬取某張圖片
import requests
import re
headers = {'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
r =requests.get("https://avatars0.githubusercontent.com/u/13572737?s=460&v=4", headers=headers)
with open('Vae.jpg','wb') as f:
f.write(r.content)
這個不僅可以爬取圖片,爬取視頻,音頻,也是這樣的
注意:爬取圖片的時候,不寫headers會報錯,找不到源
下面是把圖片保存到制定位置的代碼,多了一個判斷文件夾是否存在,不存在就創建文件夾的操作,我是根據圖片的鏈接截取作為文件夾的名稱的,使用創建文件夾的os需要導入os
import os
def get_allpath(imgurl):
info=imgurl[imgurl.index('uploads'):]
infos=info.split('/')
return infos[0]+"\\"+infos[1]+infos[2]+"\\"+infos[3]
def get_path(imgurl):
info=imgurl[imgurl.index('uploads'):]
infos=info.split('/')
return infos[0]+"\\"+infos[1]+infos[2]
def create_makedirs(dirpath):
if not os.path.exists(dirpath):
os.makedirs(dirpath)
def get_image(child, path):
for img in child.find_all(name='img'):
imgurl = img.attrs.get('data-lazy-src')
if imgurl:
if 'gif' in imgurl:
create_makedirs(path+get_path(imgurl))
r = requests.get(imgurl,headers=headers)
with open(path+get_allpath(imgurl), 'wb') as f:
f.write(r.content)
#最后我在調用的時候,直接輸入放到哪里的路徑即可
path = 'D:\\datasheetcafe\\'
get_image(child, path)
爬取視頻
其實和爬取圖片是一樣的,只不過換了url而已,這里以爬取我的嗶哩嗶哩視頻為例
如圖是我的嗶哩嗶哩發的一個視頻,點擊F12,然后在NetWork里面找到視頻的那個請求,一般是最大Size的那個
點進去,看到的那一串就是視頻地址
復制視頻地址到python爬蟲里面,改一下存儲為.mp4
import requests
import re
headers = {'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac 0S X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/52.0.2743.116 Safari/537.36'}
r =requests.get("https://113-219-141-2.ksyungslb.com/upos-sz-mirrorks32u.acgvideo.com/upgcxcode/63/76/19157663/19157663-1-48.mp4?e=ig8euxZM2rNcNbKVhwdVhoMMhwdVhwdEto8g5X10ugNcXBlqNxHxNEVE5XREto8KqJZHUa6m5J0SqE85tZvEuENvNC8xNEVE9EKE9IMvXBvE2ENvNCImNEVEK9GVqJIwqa80WXIekXRE9IMvXBvEuENvNCImNEVEua6m2jIxux0CkF6s2JZv5x0DQJZY2F8SkXKE9IB5QK==&deadline=1562826409&gen=playurl&nbs=1&oi=1947754487&os=ks3u&platform=pc&trid=28dcba5166d84f6f8b078fccbdd41f2e&uipk=5&upsig=cae257a7f3504f28137a3036304288df&uparams=e,deadline,gen,nbs,oi,os,platform,trid,uipk&mid=32059965&ksy_gslb_referer=https%3A%2F%2Fwww.bilibili.com%2Fvideo%2Fav11592898", headers=headers)
with open('Vae.mp4','wb') as f:
f.write(r.content)
網頁數量少的動態網站的爬蟲
網站介紹
這個網站是這樣的,他的信息是js動態加上去的,而且必須登錄之后才能看到數據
所以我使用BeautifulSoup在線爬不行,我不會
使用了Selenium登錄了,也動態加載頁面了,數據也出來了,但是這個網站特別的不規則,
都沒有class,我不好找規律,也是不會寫......
解決辦法
大佬給我提出個辦法,因為這個網站的頁面不多,也就20幾個,讓我直接保存到本地,然后讀取本地的html爬蟲,這樣就可以使用BeautifulSoup了,不錯哦
我的智障做法
我直接在網頁上按下了ctrl+s,保存到了本地.........
注意啊!! ctrl+s保存的網頁還是沒有數據的啊,還是需要js加載的啊......我忙活了半天,白忙活了
正確的做法
大佬得知此事之后,痛心疾首,說我早就和你說了,復制DOM就可以了,你不聽 🐷
對此我只能說,我是🐷,我是🐷,我是🐷
正確的做法是,選擇這該死的DOM,右鍵,復制HTML,這樣他的數據啥的,全都有
我是一個一個復制了,然后保存到我的本地html了
如果是很多的話,成百上千的網頁,這樣就不適合手動了,可以使用Selenium加載之后保存到本地
python爬蟲代碼
我不會放出所有的,寫寫重點
爬蟲爬本地HTML
首先要安裝一個爬本地HTML的包
pip install requests-file
from requests_file import FileAdapter
def get_one_page(file_name):
s = requests.Session()
s.mount('file://', FileAdapter())
html = s.get(f'file:///D:/'+file_name+'.html')
soup = BeautifulSoup(html, 'lxml')
...省略...