學習python-爬蟲
阿新 • • 發佈:2022-12-08
爬蟲04
1.爬蟲介紹
- python是做爬蟲比較方便,很多爬蟲的庫。其次java、go
- http協議
- pc端、小程式、app
- 模擬傳送http請求,拿到返回資料然後解析出我們想要的資料,最後儲存氣起來。
- requests,selenium bs4
2.request模組使用
- 傳送get請求
- 請求地址中帶資料:直接拼、params引數
- 編碼問題、url編碼
- 攜帶請求頭:user-agent、referer
- 攜帶cookie:登入後cookie、就是登入狀態
- 帶在請求頭中
- 帶在cookie引數中:字典、cookiejar物件
- 傳送post請求
- 攜帶資料:data、json、傳檔案型別
- Response物件的屬性和方法
- text
- headers
- cookies
- content:檔案
3.代理
- 封ip、代理
- 代理ip:收費
- 開源的代理池
4.requests爬取視訊
- referer,視訊地址還需要轉換
5.bs4解析xml庫
- html是xml的一種
6.bs4遍歷
- .
- 取屬性
- 取文字:text、string、strings
- 巢狀使用
- 父親、兄弟,子孫...
7.bs4搜尋
- 可以和遍歷連用
- find、find_all
- 5種搜尋方式:字串、布林、正則、列表、方法
8.css選擇器
- 標籤名
- 類名
- id
9.selenium
- 控制瀏覽器、瀏覽器驅動跟瀏覽器版本對應
- python操作瀏覽器
- 無頭瀏覽器
- 等待
- 搜尋方式:
find_element(by='', value='')
- css選擇器:CSS_SELECTOR
- xpath
- 獲取cookie
- 將cookie儲存到本地
- 開啟頁面,把cookie寫入
10.抽屜半自動點贊
- 使用requests傳送點贊請求,需要攜帶cookie
- 先使用selenium登入進去,然後拿到cookie
- 最後用requests使用自動點贊。
一、xpath的使用
-
html中選擇標籤可以使用的通用方式有2種:css選擇器和xpath選擇。
XPath即XML路徑語言(XML Path Language),它是一種用來確定XML文件中某部分位置的語言
-
語法的簡單介紹
nodename 選取此節點的所有子節點 / 從根節點選取 /body/div // 從匹配選擇當前節點,而不考慮它們的位置 //div . 選取當前節點 .. 選取當前節點的父節點 @ 選取屬性
-
最簡單的方式copy複製
doc = '''
<html>
<head>
<base href='http://example.com/' />
<title>Example website</title>
</head>
<body>
<div id='images'>
<a href='image1.html' id='id_a'>Name: My image 1 <br/><img src='image1_thumb.jpg' /></a>
<a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
<a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
<a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
<a href='image5.html' class='li li-item' name='items'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
<a href='image6.html' name='items'><span><h5>test</h5></span>Name: My image 6 <br /><img src='image6_thumb.jpg' /></a>
</div>
</body>
</html>
'''
from lxml import etree
html = etree.HTML(doc)
# html = etree.parse('search.html', etree.HTMLParser())
# 1.所有節點
a = html.xpath('//*')
# 2.指定節點(結果為列表)
b = html.xpath('//head')
# 3.子節點,子孫節點
c = html.xpath('//div/a')
d = html.xpath('//body/a') # 子節點沒有a則沒有資料
e = html.xpath('//body//a')
# 4.父節點
aa = html.xpath('//body//a[@href="image1.html"]/..')
bb = html.xpath('//body//a[1]/..')
# 也可以這樣
cc = html.xpath('//body//a[1]/parent::*')
dd = html.xpath('//body//a[1]/parent::div')
# 5.屬性匹配
ee = html.xpath('//body//a[@href="image1.html"]')
# 6.文字獲取 text()
ff = html.xpath('//body//a[@href="image1.html"]/text()')
# 7.屬性獲取 @
g = html.xpath('//body//a/@href')
gg = html.xpath('//body//a/@id')
# 8.屬性多值匹配
# a標籤有多個class類,直接匹配就不行,需要yogacontains
h = html.xpath('//body//a[@class="li"]')
hh = html.xpath('//body//a[@name="items"]')
i = html.xpath('//body//a[contains(@class,"li")]')
ii = html.xpath('//body//a[contains(@class, "li")]/text()')
# 多屬性匹配
j = html.xpath('//body//a[contains(@class, "li")or @name="items"]')
jj = html.xpath('//body//a[contains(@class, "li") and @name="items"]/text()')
#10.按序選擇
k = html.xpath('//a[2]/text()')
kk = html.xpath('//a[3]/@href')
# 取最後一個
l = html.xpath('//a[last()]/@href')
# 位置小於3
ll = html.xpath('//a[position() < 3]/@href')
# 倒數第二個
m = html.xpath('//a[last()-2]/@href')
# 節點軸選擇
# 祖先節點:ancestor
# 使用了* 獲取所有祖先節點
mm = html.xpath('//a/ancestor::*')
# 獲取祖先節點中的div
n = html.xpath('//a/ancestor::div')
# 屬性值:attribute
nn = html.xpath('//a[1]/attribute::*')
o = html.xpath('//a[1]/attribute::href')
# 直接子節點:child
oo = html.xpath('//a[1]/child::*')
# 所有子孫節點:descendant
p = html.xpath('//a[6]/descendant::*')
#當前節點之後所有節點
pp = html.xpath('//a[1]/following::*')
# q = html.xpath('//a[1]following::*[1]/@href')
# 當前節點之後同級節點:following-sibing
qq = html.xpath('//a[1]/following-sibling::*')
s = html.xpath('//a[1]/following-sibling::a')
ss = html.xpath('//a[1]/following-sibling::*[2]')
sss = html.xpath('//a[1]/following-sibling::*[2]/@href')
print(b)
二、selenium動作鏈
-
網站中有些按住滑鼠,滑動的效果===》滑動驗證碼
-
兩種形式
-
形式一:
actions = ActionChains(bro) # 拿到動作鏈物件 actions.drag_and_drop(sourse, target) # 把動作放到動作鏈中,準備序列執行 actions.perform()
-
形式二:
ActionChains(bro).click_and_hold(sourse).perform() distance = target.location['xxx']-sourse.location['xx'] track = 0 while track < distance: ActionChains(bro).move_by_offset(xoffset=2, yoffset=0).perform() track +=2
import target as target from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver import ActionChains import time bro = webdriver.Chrome(executable_path='./chromedriver.exe') bro.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable') bro.implicitly_wait(10) try: bro.switch_to.frame('iframeResult') ## 切換到iframeResult sourse = bro.find_element(by=By.ID, value='draggable') target -= bro.find_element(by=By.ID, value='droppable') # 方式一:基於同一個動作鏈序列執行 # actions = ActionChains(bro) # 拿到動作鏈物件 # actions.drag_and_drop(sourse, target) # 把動作放在動作鏈中,準備序列執行 # actions.perform() # 方式二:不同的動作鏈,每次移動的位移都不同 ActionChains(bro).click_and_hold(sourse).perform() distance = target.location['x'] - sourse.location['x'] print('目標距離源的x軸距離:', distance) track = 0 while track < distance: ActionChains(bro).move_by_offset(xoffset=2, yoffset=0).perform() track += 2 ActionChains(bro).release().perform() time.sleep(10) except Exception as e: print(e) finally: bro.close()
-
三、自動登入12306
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--disable-blink-features=AutomationControlled") # 去掉自動化控制的提示
bro = webdriver.Chrome(executable_path='./chromedriver.exe', options=options)
bro.get('https://kyfw.12306.cn/otn/resources/login.html')
bro.maximize_window()
# 12306檢測到我們使用了selenium控制了瀏覽器,所以它的滑塊出不來
bro.implicitly_wait(10)
try:
username = bro.find_element(by=By.ID, value='J-userName')
username.send_keys('13735760305')
password = bro.find_element(by=By.ID, value='J-password')
password.send_keys('xcc123')
time.sleep(3)
btn = bro.find_element(by=By.ID, value='J-login')
btn.click()
span = bro.find_element(by=By.ID, value='nc_1_n1z')
ActionChains(bro).click_and_hold(span).perform() # 滑鼠點主
ActionChains(bro).move_by_offset(xoffset=300, yoffset=0).perform() # 滑動
time.sleep(10)
except Exception as e:
print(e)
finally:
bro.close()
注意:
* 有的網站可以檢測到使用了自動化控制所以我們需要加一行程式碼:options.add_argument("--disable-blink-features=AutomationControlled")
四、打碼平臺使用
- 使用打碼平臺自動登入``
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from chaojiying import ChaojiyingClient
from PIL import Image
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('http://www.chaojiying.com/apiuser/login/')
bro.implicitly_wait(10)
bro.maximize_window()
try:
username = bro.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[3]/div[1]/form/p[1]/input')
password = bro.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[3]/div[1]/form/p[2]/input')
code = bro.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[3]/div[1]/form/p[3]/input')
btn = bro.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[3]/div[1]/form/p[4]/input')
username.send_keys('306334678')
password.send_keys('lqz123')
# 獲取驗證碼:
#1 整個頁面截圖
bro.save_screenshot('main.png')
# 2 使用pillow,從整個頁面中截取出驗證碼圖片 code.png
img = bro.find_element(By.XPATH, '/html/body/div[3]/div/div[3]/div[1]/form/div/img')
location = img.location
size = img.size
print(location)
print(size)
# 使用pillow扣除大圖中的驗證碼
img_tu = (int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
# # 摳出驗證碼
# #開啟
img = Image.open('./main.png')
# 摳圖
fram = img.crop(img_tu)
# 截出來的小圖
fram.save('code.png')
# 3 使用超級鷹破解
chaojiying = ChaojiyingClient('306334678', 'lqz123', '937234') # 使用者中心>>軟體ID 生成一個替換 96001
im = open('code.png', 'rb').read() # 本地圖片檔案路徑 來替換 a.jpg 有時WIN系統須要//
print(chaojiying.PostPic(im, 1902)) # 1902 驗證碼型別 官方網站>>價格體系 3.4+版 print 後要加()
res_code=chaojiying.PostPic(im, 1902)['pic_str']
code.send_keys(res_code)
time.sleep(5)
btn.click()
time.sleep(10)
except Exception as e:
print(e)
finally:
bro.close()
五、使用selenium爬取京東商品資訊
from selenium import webdriver
from selenium.webdriver.common.by import By # 按照什麼方式查詢,By.ID,By.CSS_SELECTOR
import time
from selenium.webdriver.common.keys import Keys
def get_goods(driver):
try:
goods = driver.find_elements(by=By.CLASS_NAME, value='gl-item')
for good in goods:
name = good.find_element(by=By.CSS_SELECTOR, value='.p-name em').text
price = good.find_element(by=By.CSS_SELECTOR, value='.p-price i').text
commit = good.find_element(by=By.CSS_SELECTOR, value='.p-commit a').text
url = good.find_element(by=By.CSS_SELECTOR, value='.p-name a').get_attribute('href')
img = good.find_element(by=By.CSS_SELECTOR, value='.p-img img').get_attribute('src')
if not img:
img ='https://'+ good.find_element(by=By.CSS_SELECTOR, value='.p-img img').get_attribute('data-lazy-img')
print('''
商品名字:%s
商品價格:%s
商品連結:%s
商品圖片:%s
商品評論:%s
''' % (name, price, url, img, commit))
button = driver.find_element(by=By.PARTIAL_LINK_TEXT, value='下一頁')
button.click()
time.sleep(1)
get_goods(driver)
except Exception as e:
print(e)
def spider(url, keyword):
driver = webdriver.Chrome(executable_path='./chromedriver.exe')
driver.get(url)
driver.implicitly_wait(10) # 使用隱式等待
try:
input_tag = driver.find_element(by=By.ID, value='key')
input_tag.send_keys(keyword)
input_tag.send_keys(Keys.ENTER)
get_goods(driver)
finally:
driver.close()
if __name__ == '__main__':
spider('https://www.jd.com/', keyword='精品內衣')
六、scrapy介紹
-
前面學的都是模組如果是做專業的爬蟲需要使用框架,類似於
django:web
-
scrapy:爬蟲框架
- 做爬蟲用的東西都封裝好了,只需要在固定的位置上寫固定的程式碼即可
-
scrapy:號稱爬蟲界的django
- django 大而全,做web相關的它都用到
- scrapy 大而全,做爬蟲相關的它都用到
-
介紹:
Scrapy一個開源和協作的框架,起初為了頁面的抓取(更準確來說,網路抓取)所設計的,使用它可以快速、簡單、可擴充套件的方式從網站中提取所需的資料。但是目前Scrapy的用途十分廣泛,可用於資料探勘、監測和自動化測試等領域,也可以應用在獲取API所返回的資料或通用的網路爬蟲
-
安裝 scrapy
* 針對mac、linux: pip3 install scrapy * 針對win:可能會出錯(看情況) pip3 install scrapy 如果安裝失敗可能作業系統缺少一些依賴。 1.pip3 install wheel # 安裝後,方便通過wheel檔案安裝軟體 xx.whl 2.pip3 insatll lxml 3.pip3 install pyopenssl 4.下載並安裝pywin32: https://sourceforge.net/projects/pywin32/files/pywin32/ 5.下載twisted的wheel檔案: http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 6.執行pip3 install 下載目錄\Twisted-17.9.0-cp36-cp36m-win_amd64.whl 7.pip3 install scrapy
-
釋放出scrapy 可執行檔案
- 以後使用這個建立爬蟲專案===》django-admin建立django專案
-
建立爬蟲專案
scrapy startproject myfirstscrapy
-
建立爬蟲【django建立app】
scrapy genspider 專案名(爬蟲名字) 爬取的地址
-
啟動爬蟲
scrapy crawl cnblogs --nolog
-
pycharm中執行
新建run.py from scrapy.cmdline import execute execute(['scrapy', 'crawl', 'cnblogs','--nolog'])
七、scrapy架構介紹
-
引擎(EGINE)
引擎負責控制系統所有元件之間的資料流,並在某些動作發生時觸發事件。有關詳細資訊,請參照上面的資料流部分。
-
排程器(SCHEDULER)
用來接受引擎發過來的請求並壓入佇列中,並在引擎再次請求的時候返回,可以想象成一個URL的優先順序佇列,由它決定下一個要抓取的網址是什麼,同時去除重複的網址
-
下載器(DOWLOADER)
用於下載網頁內容並將網頁內容返回給EGINE,下載器是建立在twisted這個高效的非同步模型上的
-
爬蟲(SPIDERS)
SPIDERS是開發人員自定義的類,用來解析responses,並且提取items或者傳送新的請求
-
專案管道(ITEM PIPLINES)
在item被提取後負責處理他們,主要包括清理、驗證、持久化(比如存到資料庫)等操作
-
下載器中介軟體(DOWLOADER Middlewares)
位於Scrapy引擎和下載器之間,主要用來處理從EGINE傳到DOWLOADER的請求request,已經從DOWNLOADER傳到EGINE的響應responses,你可以用該中介軟體做一下幾件事情: 1.設定請求, 2.設定cookie 3.使用代理 4.整合selenium
-
爬蟲中介軟體(Spider Middlewares)
位於EGINE和SPIDERS之間,主要工作是處理SPIDERS的輸入(即responses)和輸出(即requests)
八、scrapy解析資料
-
response物件有css方法和xpath方法
- css中寫css選擇器
- xpath中寫xpath選擇
-
重點1
* xpath取文字內容 './/a[contains(@class,"link-title")]/text()' * xpath取屬性 './/a[contains(@class,"link-title")]/@href' * css取文字 'a.link-title::text' * css取屬性 'img.image-scale::attr(src)'
-
重點2
.extract_first() 取一個 .extract() 取所有
import scrapy
from bs4 import BeautifulSoup
class CnblogsSpider(scrapy.Spider):
name = 'cnblogs'
allowed_domains = ['www.cnblogs.com']
start_urls = ['http://www.cnblogs.com/']
def parse(self, response):
# response類似於requests模組的response物件
# print(response.text)
# 返回的資料,解析資料
方式1:使用bs4(不用了)
# 方式一:使用bs4
soup = BeautifulSoup(response.text, 'lxml')
article_list = soup.find_all(class_='post-item')
for article in article_list:
title_name = article.find(name='a', class_='post-item-title').text
print(title_name)
方式2:scrapy自帶的解析> css解析
article_list = response.css('article.post-item')
for article in article_list:
title_name = article.css('section>div>a::text').extract_first()
author_img = article.css('p.post-item-summary>a>img::attr(src)').extract_first()
desc_list = article.css('p.post-item-summary::text').extract()
desc = desc_list[0].replace('\n', '').replace(' ', '')
if not desc:
desc = desc_list[1].replace('\n', '').replace(' ', '')
author_name = article.css('section>footer>a>span::text').extract_first()
article_date = article.css('section>footer>span>span::text').extract_first()
# 文章詳情內容,因為在下一頁,先不考慮
print('''
文章標題:%s
作者標題:%s
摘要:%s
作者名字:%s
釋出日期:%s
''' % (title_name, author_img, desc, author_name, article_date))
方式3:scrapy自帶的解析> xpath解析
article_list = response.xpath('//article[contains(@class,"post-item")]')
for article in article_list:
title_name = article.xpath('./section/div/a/text()').extract_first()
author_img = article.xpath('./section/div/p//img/@src').extract_first()
desc_list = article.xpath('./section/div/p/text()').extract()
desc = desc_list[0].replace('\n', '').replace(' ', '')
if not desc:
desc = desc_list[1].replace('\n', '').replace(' ', '')
author_name = article.xpath('./section/footer/a/span/text()').extract_first()
article_date = article.xpath('./section/footer/span/span/text()').extract_first()
print('''
文章標題:%s
作者標題:%s
摘要:%s
作者名字:%s
釋出日期:%s
''' % (title_name, author_img, desc, author_name, article_date))
九、settings相關配置,提高爬取效率
基礎配置
# 爬蟲專案名
BOT_NAME = 'myfirstscrapy'
# 指定爬蟲類的py檔案的位置
SPIDER_MODULES = ['myfirstscrapy.spiders']
NEWSPIDER_MODULE = 'myfirstscrapy.spiders'
# 使用者型別(攜帶在請求頭裡)
USER_AGENT = 'myfirstscrapy (+http://www.yourdomain.com)'
# Obey robots.txt rules==>是否遵循爬蟲協議
ROBOTSTXT_OBEY = True
# LOG_LEVEL 日誌級別
LOG_LEVEL = 'ERROR' # 報錯如果不列印日誌,在控制檯看不到錯誤
# 啟動執行緒數(多少個爬蟲同時工作)
CONCURRENT_REQUESTS = 32
# Override the default request headers:==>預設請求頭
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
# 爬蟲中介軟體
SPIDER_MIDDLEWARES = {
'myfirstscrapy.middlewares.MyfirstscrapySpiderMiddleware': 543,
}
# 下載中介軟體
DOWNLOADER_MIDDLEWARES = {
'myfirstscrapy.middlewares.MyfirstscrapyDownloaderMiddleware': 543,
}
# 持久化配置
ITEM_PIPELINES = {
'myfirstscrapy.pipelines.MyfirstscrapyPipeline': 300,
}
增加爬蟲的爬取效率
1.增加併發 預設開啟執行緒數為16
預設scrapy開啟的併發執行緒為32個,可以適當進行增加。在settings配置檔案中修改
CONCURRENT_REQUESTS = 100
值為100相當於併發設定成了100.
2.降低日誌級別
在執行scrapy時會有大量日誌資訊的輸出。為了減少CPU的使用率。可以設定log輸出資訊為INFO或者ERROR即可。在settings配置檔案寫:
LOG_LEVEL = 'INFO'
3.禁止cookie
如果不是真的需要cookie,則在scrapy爬取資料時候禁止cookie從而減少CPU的使用率,提高爬取效率。在配置檔案中編寫:
COOKIES_ENABLED = False
4.禁止重試
對失敗的HTTP進行重新請求(重試)會減少爬取速度,因此可以禁止重試。在配置檔案中編寫:
RETRY_ENABLED = False
5.減少下載超時
如果對用一個非常慢的連結進行爬取,減少下載超時可以讓卡住的連結快速被放棄,從而提高效率。在配置檔案中降序編寫:
DOWNLOAD_TIMEOUT = 10s
十、 持久化方案
-
儲存到硬碟上
-
兩種方案:第二種常用
-
第一種:瞭解
* 解析函式中parse,需要return [{}, {}, {}] # 列表套字典 * scrapy crawl cnblogs -o 檔名(json, pickle, csv結尾的檔案)
-
第二種:使用
pipeline
常用的管道形式,可以同時存多個位置的。1. 在items.py 中寫一個類[相當於django的表模型],繼承scrapy.Item 2. 在類中寫屬性,寫欄位,所有欄位都是scrapy.Field型別 eg: title = scrapy.Field() 3. 在爬蟲中匯入類,例項化得到物件,把要儲存的資料放到物件中 item['title'] = title 【注意:不能用 . 的形式去獲取】 解析類中 yield item 4. 修改配置檔案,指定pipline,數字表示優先順序,越小則優先順序越大 ITEM_PIPELINES = { 'crawl_cnblogs.pipelines.CrawlCnblogsPipeline': 300, } 5. 寫一個pipline:CrawlCnblogsPipeline * open_spider:資料初始化,開啟檔案,開啟資料庫連結 * process_item:真正儲存的地方 注意: 最後需要return item,交給後續pipeline繼續使用 * close——spider:銷燬資源,關閉檔案,關閉資料庫連結
-
十一、全站爬取cnblogs文章
- 第一頁爬完後,要儲存的資料已經儲存
- 之後需要歐兩件事情:
- 繼續爬取下一頁資料===>解析洗衣液的地址,包裝成request物件
- 繼續爬取詳情頁===>解析出詳情頁的地址,包裝成request物件
- 現在在這裡不能儲存,因為資料不全,缺少文章詳情,我們需要加上文章詳情後再一次性儲存。
request和response物件傳遞引數
-
Request建立
-
在parse中,for迴圈中,建立Request物件時,傳入meta
yield Request(url=url, callback=self.detail_parse,meta={'item':item})
-
-
Response物件
-
detail_parse中,通過response取出meta再取出meta裡面的item,把文章詳情寫入。
yield item
-
解析下一頁並繼續爬取
spiders/cnblogs.py
from scrapy import Request
from myfirstscrapy.items import CnblogsItem
import scrapy
from bs4 import BeautifulSoup
class CnblogsSpider(scrapy.Spider):
name = 'cnblogs'
allowed_domains = ['www.cnblogs.com']
start_urls = ['http://www.cnblogs.com/']
def parse(self, response):
# item = CnblogsItem() # 外面定義,會有問題
article_list = response.xpath('//article[contains(@class,"post-item")]')
for article in article_list:
item = CnblogsItem() # 定義在for內部,每次都是一個新物件
title_name = article.xpath('./section/div/a/text()').extract_first()
author_img = article.xpath('./section/div/p//img/@src').extract_first()
desc_list = article.xpath('./section/div/p/text()').extract()
desc = desc_list[0].replace('\n', '').replace(' ', '')
if not desc:
desc = desc_list[1].replace('\n', '').replace(' ', '')
author_name = article.xpath('./section/footer/a/span/text()').extract_first()
article_date = article.xpath('./section/footer/span/span/text()').extract_first()
url = article.xpath('./section/div/a/@href').extract_first()
item['title_name'] = title_name
item['author_img'] = author_img
item['desc'] = desc
item['author_name'] = author_name
item['article_date'] = article_date
item['url'] = url
# print(url)
# 現在不存了,因為資料不全,等全了以後再存,繼續爬取,就要建立Request物件
# 詳情頁面,使用self.detail_parse解析
yield Request(url=url, callback=self.detail_parse, meta={'item': item})
# 解析出下一頁地址
# css
next_url = 'https://www.cnblogs.com' + response.css('div.pager>a:last-child::attr(href)').extract_first()
print(next_url)
yield Request(url=next_url, callback=self.parse)
def detail_parse(self, response):
# print(len(response.text))
item = response.meta.get('item')
# 解析詳情
article_content = response.css('div.post').extract_first()
# print(article_content)
# print('===================')
# 把詳情,寫入當前meta中得item中
item['article_content'] = str(article_content)
yield item
items.py
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class CnblogsItem(scrapy.Item):
# # define the fields for your item here like:
# # name = scrapy.Field()
# pass
title_name = scrapy.Field()
author_img = scrapy.Field()
desc = scrapy.Field()
author_name = scrapy.Field()
article_date = scrapy.Field()
url = scrapy.Field()
article_content = scrapy.Field() # 文章詳情
pipelines.py
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
import pymysql
from itemadapter import ItemAdapter
class CnblogsMysqlPipeline:
def open_spider(self, spider):
# 開啟資料庫連結
self.conn = pymysql.connect(
user='root',
password='123',
host='127.0.0.1',
database='cnblogs',
port=3306,
autocommit=True # 自動提交,配置後就不用在下面新增 self.conn.commit()
)
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
self.cursor.execute(
'insert into artice (title_name,author_img,`desc`,author_name,article_date,url,article_content) values (%s,%s,%s,%s,%s,%s,%s)',
args=[item['title_name'], item['author_img'], item['desc'], item['author_name'], item['article_date'],
item['url'], item['article_content']])
# self.conn.commit() # 提交
return item
# 關閉資料庫
def close_spider(self, spider):
self.cursor.close()
self.conn.close()
class CnblogsFilesPipeline:
def open_spider(self, spider):
# cnblogs這個爬蟲物件
self.f = open('cnblogs.txt', 'at', encoding='utf-8')
def process_item(self, item, spider):
# 存真正的資料,每次item都會走一次
print(item)
# 存檔案不用這種方式,最好爬蟲啟動,把檔案開啟爬蟲結束檔案就關閉了
with open('cnblogs.txt', 'at', encoding='utf-8') as f:
f.write('文章標題:%s, 文章作者:%s\n' % (item['title_name'], item['author_name']))
# return item
self.f.write('文章標題:%s, 文章作者:%s\n' % (item['title_name'], item['author_name']))
return item
def close_spider(self, spider):
print('我關了')
self.f.close()
十二、爬蟲和下載中介軟體
-
scrapy的所有中介軟體都寫在middlewares.py中,跟django非常像,做一些攔截。
-
爬蟲中介軟體(用的很少,瞭解即可)
MyfirstscrapySpiderMiddleware def process_spider_input(self, response, spider): # 進入爬蟲會執行它 def process_spider_output(self, response, result, spider): #從爬蟲出來會執行它 def process_spider_exception(self, response, exception, spider):#出了異常會執行 def process_start_requests(self, start_requests, spider):#第一次爬取執行 def spider_opened(self, spider): #爬蟲開啟執行 # 下載中介軟體 MyfirstscrapyDownloaderMiddleware def process_request(self, request, spider): # request物件從引擎進入到下載器會執行 def process_response(self, request, response, spider):# response物件從下載器進入到引擎會執行 def process_exception(self, request, exception, spider):#出異常執行它 def spider_opened(self, spider): #爬蟲開啟執行它
重點:process_request, process_response
下載中介軟體的process_request
* 返回值 * return None:繼續執行下面的中介軟體的process_request * return a Response object:不進行下載中介軟體,直接返回給引擎,引擎把它給爬蟲 * return a Request object:不進入下載中介軟體,直接返回給引擎,引擎把它放到排程器中。 * raise IgnoreRequest:process_exception() 拋異常,會執行process_exception
下載中介軟體的process_response
* 返回值 * return a Response object:正常,會進入到引擎,引擎會把它給爬蟲 * return a Request object:會進入到引擎,引擎會把它放進排程器中, 等待下次被爬 * raise IgnoreRequest 會執行process_exception