摘要:下一步是審查收到的代碼,然后從里面提取圖片。我們再次創建一個實例,然后發起一個請求存儲圖片到磁盤上到達的響應攜帶了請求的圖片內容。此外,我們還需要提供一個目錄存放下載的圖片這是更新后的構造函數好的,現在我們準備保存文件到磁盤上。
什么是網頁抓取?
你是否曾經需要從一個沒有提供 API 的站點獲取信息? 我們可以通過網頁抓取,然后從目標網站的 HTML 中獲得我們想要的信息,進而解決這個問題。 當然,我們也可以手動提取這些信息, 但手動操作很乏味。 所以, 通過爬蟲來自動化來完成這個過程會更有效率。
在這個教程中我們會從?Pexels?抓取一些貓的圖片。這個網站提供高質量且免費的素材圖片。他們提供了API, 但這些 API 有 200次/小時 的請求頻率限制。
[](https://user-gold-cdn.xitu.io...
發起并發請求在網頁抓取中使用異步 PHP (相比使用同步方式)的最大好處是可以在更短的時間內完成更多的工作。使用異步 PHP 使得我們可以立刻請求盡可能多的網頁而不是每次只能請求單個網頁并等待結果返回。 因此,一旦請求結果返回我們就可以開始處理。
首先,我們從 GitHub 上拉取一個叫做?buzz-react? 的異步 HTTP 客戶端的代碼 -- 它是一個基于 ReactPHP 的簡單、致力于并發處理大量 HTTP 請求的異步 HTTP 客戶端:
composer require clue/buzz-react
現在, 我們就可以請求?pexels 上的圖片頁面?了:
get("https://www.pexels.com/photo/kitten-cat-rush-lucky-cat-45170/") ->then(function(PsrHttpMessageResponseInterface $response) { echo $response->getBody(); }); $loop->run();
我們創建了?ClueReactBuzzBrowser?的實例, 把它作為 HTTP client 使用。上面的代碼發起了一個異步的?GET?請求來獲取網頁內容(包含一張小貓們的圖片)。?$client->get($url)?方法返回了一個包含?PSR-7 response 的?promise?對象。
客戶端是異步工作的,這意味著我們可以很容易地請求幾個頁面,然后這些請求會被同步執行:
get("https://www.pexels.com/photo/kitten-cat-rush-lucky-cat-45170/") ->then(function(PsrHttpMessageResponseInterface $response) { echo $response->getBody(); }); $client->get("https://www.pexels.com/photo/adorable-animal-baby-blur-177809/") ->then(function(PsrHttpMessageResponseInterface $response) { echo $response->getBody(); }); $loop->run();
這里的代碼含義如下:
發起一個請求
獲取響應
添加響應的處理程序
當響應解析完畢就處理響應
所以,這個邏輯可以提取到一個類里,這樣我們可以很容易地請求多個 URL 并添加相同的響應處理程序。讓我們基于Browser創建一個包裝器。
用下面的代碼創建一個名為Scraper的類:
client = $client; } public function scrape(array $urls) { foreach ($urls as $url) { $this->client->get($url)->then( function (ResponseInterface $response) { $this->processResponse((string) $response->getBody()); }); } } private function processResponse(string $html) { // ... } }
我們把Browser作為依賴項注入到構造函數并提供一個公共方法scrape(array $urls)。接著對每個指定的 URL 發起一個GET請求。當響應完成時,我們調用一個私有方法processResponse(string $html)。這個方法負責遍歷 HTML 代碼并下載圖片。下一步是審查收到的 HTML 代碼,然后從里面提取圖片。
發起并發請求在網頁抓取中使用異步 PHP (相比使用同步方式)的最大好處是可以在更短的時間內完成更多的工作。使用異步 PHP 使得我們可以立刻請求盡可能多的網頁而不是每次只能請求單個網頁并等待結果返回。 因此,一旦請求結果返回我們就可以開始處理。
首先,我們從 GitHub 上拉取一個叫做?buzz-react? 的異步 HTTP 客戶端的代碼 -- 它是一個基于 ReactPHP 的簡單、致力于并發處理大量 HTTP 請求的異步 HTTP 客戶端:
composer require clue/buzz-react
現在, 我們就可以請求?pexels 上的圖片頁面?了:
get("https://www.pexels.com/photo/kitten-cat-rush-lucky-cat-45170/") ->then(function(PsrHttpMessageResponseInterface $response) { echo $response->getBody(); }); $loop->run();
我們創建了?ClueReactBuzzBrowser?的實例, 把它作為 HTTP client 使用。上面的代碼發起了一個異步的?GET?請求來獲取網頁內容(包含一張小貓們的圖片)。?$client->get($url)?方法返回了一個包含?PSR-7 response 的?promise?對象。
客戶端是異步工作的,這意味著我們可以很容易地請求幾個頁面,然后這些請求會被同步執行:
get("https://www.pexels.com/photo/kitten-cat-rush-lucky-cat-45170/") ->then(function(PsrHttpMessageResponseInterface $response) { echo $response->getBody(); }); $client->get("https://www.pexels.com/photo/adorable-animal-baby-blur-177809/") ->then(function(PsrHttpMessageResponseInterface $response) { echo $response->getBody(); }); $loop->run();
這里的代碼含義如下:
發起一個請求
獲取響應
添加響應的處理程序
當響應解析完畢就處理響應
所以,這個邏輯可以提取到一個類里,這樣我們可以很容易地請求多個 URL 并添加相同的響應處理程序。讓我們基于Browser創建一個包裝器。
用下面的代碼創建一個名為Scraper的類:
client = $client; } public function scrape(array $urls) { foreach ($urls as $url) { $this->client->get($url)->then( function (ResponseInterface $response) { $this->processResponse((string) $response->getBody()); }); } } private function processResponse(string $html) { // ... } }
我們把Browser作為依賴項注入到構造函數并提供一個公共方法scrape(array $urls)。接著對每個指定的 URL 發起一個GET請求。當響應完成時,我們調用一個私有方法processResponse(string $html)。這個方法負責遍歷 HTML 代碼并下載圖片。下一步是審查收到的 HTML 代碼,然后從里面提取圖片。
爬取網站此刻我們只是獲取到了響應頁面的 HTML 代碼。現在需要提取圖片 URL。為此,我們需要審查收到的 HTML 代碼結構。前往?Pexels 的圖片頁,右擊圖片并選擇審查元素,你會看到一些東西,就像這樣:
[](https://user-gold-cdn.xitu.io...
我們可以看到img標簽有個image-section__image類名。我們要使用這個信息從收到的 HTML 中提取這個標簽。圖片的 URL 存儲在src屬性里:
[](https://user-gold-cdn.xitu.io...
為提取 HTML 標簽,我們需要使用 ?Symfony 的 DomCrawler 組件。拉取需要的包:
composer require symfony/dom-crawler composer require symfony/css-selector
DomCrawler 的適配組件 CSS-selector??允許我們使用類 - jQuery 的選擇器遍歷 DOM。當安裝好一切之后,打開我們的Scraper類,在processResponse(string $html)?方法里書寫一些代碼。首先,我們需要創建一個SymfonyComponentDomCrawlerCrawler?類的實例,它的構造函數接受一個用于遍歷的 HTML 代碼字符串:
通過類 - jQuery 選擇器查找任意元素時,請使用filter()方法。然后,attr($attribute)方法允許提取已過濾元素的某個屬性:
filter(".image-section__image")->attr("src"); echo $imageUrl . PHP_EOL; } }讓我們只打印提取出的圖片 URL,檢查下我們的 scraper 是否如期工作:
scrape([ "https://www.pexels.com/photo/adorable-animal-blur-cat-617278/" ]); $loop->run();當運行這個腳本時,將會輸出所需圖片的完整 URL。然后我們要使用這個 URL 下載該圖片。 我們再次創建一個Browser實例,然后發起一個GET請求:
filter(".image-section__image")->attr("src"); $this->client->get($imageUrl)->then( function(ResponseInterface $response) { // 存儲圖片到磁盤上 }); } }到達的響應攜帶了請求的圖片內容。現在我們需要把它保存到磁盤上。但是請花費一點時間,不要使用file_put_contents()。所有的原生 PHP 函數都在文件系統下阻塞式運行。這意味著一旦你調用了file_put_contents(),我們的應用就會停止異步行為。然后流程控制會被阻塞直到文件保存完畢。ReactPHP 有個專門的包可以解決這個問題。
異步保存文件要以非阻塞方式異步處理文件的話,我們需要一個叫做?reactphp/filesystem?的包。拉取下來:
composer require react/filesystem要異步使用文件系統,請創建一個Filesystem對象并把它作為依賴項提供給Scraper。此外,我們還需要提供一個目錄存放下載的圖片:
scrape([ "https://www.pexels.com/photo/adorable-animal-blur-cat-617278/" ]); $loop->run();這是更新后Scraper的構造函數:
client = $client; $this->filesystem = $filesystem; $this->$directory = $directory; } // ... }好的,現在我們準備保存文件到磁盤上。首先,我們需要從 URL 提取文件名。圖片的 URL 看起來就像這樣:
https://images.pexels.com/pho...這些 URL 的文件名是這樣的:
jumping-cute-playing-animals.jpg
pexels-photo-617278.jpeg讓我們使用正則表達式從 URL 里提取出文件名。為了給磁盤上的未來文件獲取完整路徑,我們用目錄把名字串聯起來:
directory . DIRECTORY_SEPARATOR . $matches[1];當我們有了一個文件路徑,就可以用它創建一個?文件?對象:
filesystem->file($filePath);此對象表示我們要使用的文件。接著調用putContents($contents)?方法并提供一個響應體(response body)字符串:
filesystem->file($filePath); $file->putContents((string)$response->getBody());就是這樣。所有異步的底層魔法隱藏在一個多帶帶的方法內。此 hook 會創建一個寫模式的流,寫入數據后關閉這個流。這是Scraper::processResponse(string $html)方法的更新版本:
filter(".image-section__image")->attr("src"); preg_match("/photos/d+/([w-.]+)?/", $imageUrl, $matches); $filePath = $matches[1]; $this->client->get($imageUrl)->then( function(ResponseInterface $response) use ($filePath) { $this->filesystem->file($filePath)->putContents((string)$response->getBody()); }); } }我們傳遞了一個完整路徑到響應的處理程序里。然后,我們創建了一個文件并填充了響應體。實際上,完整的Scraper只有不到 50 行的代碼!
注意:在你想存儲文件的位置先創建目錄。putContents()?方法只創建文件,不會為指定的文件創建文件夾。scraper 完成了。現在,打開你的主腳本,給scrape方法傳遞一個 URL 列表:
scrape([ "https://www.pexels.com/photo/adorable-animal-blur-cat-617278/", "https://www.pexels.com/photo/kitten-cat-rush-lucky-cat-45170/", "https://www.pexels.com/photo/adorable-animal-baby-blur-177809/", "https://www.pexels.com/photo/adorable-animals-cats-cute-236230/", "https://www.pexels.com/photo/relaxation-relax-cats-cat-96428/", ]); $loop->run();上面的代碼爬取 5 個 URL 并下載相應圖片。所有這些工作會快速地異步完成。
[](https://user-gold-cdn.xitu.io...
結尾在?上一個教程里,我們使用 ReactPHP 加速網站抓取過程并同時查詢頁面。但是,如果我們也需要同時保存文件呢?在異步的應用程序中,我們不能使用諸如file_put_contents()的原生 PHP 函數,因為它們會阻塞程序流程,所以在磁盤上存儲圖片不會有任何加速。想要在 ReactPHP 里以異步 - 非阻塞的方式處理文件時,我們需要使用?reactphp/filesystem?包。
所以,在上面 50 行的代碼里,我們就能加速網站抓取并運行起來。這只是一個你也可以做的簡潔例子。現在你有了怎樣構建爬蟲的基礎知識,請嘗試做一個自己的吧!
我還有一些用 ReactPHP 抓取網站的文章:如果你想?使用代理?或者?限制并發請求的數量,可以閱讀一下。
*
你也可以從?GitHub?找到這篇文章的例子。
轉自 PHP / Laravel 開發者社區 https://laravel-china.org/top...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/29965.html
摘要:且本小白也親身經歷了整個從小白到爬蟲初入門的過程,因此就斗膽在上開一個欄目,以我的圖片爬蟲全實現過程為例,以期用更簡單清晰詳盡的方式來幫助更多小白應對更大多數的爬蟲實際問題。 前言: 一個月前,博主在學過python(一年前)、會一點網絡(能按F12)的情況下,憑著熱血和興趣,開始了pyth...
摘要:時間永遠都過得那么快,一晃從年注冊,到現在已經過去了年那些被我藏在收藏夾吃灰的文章,已經太多了,是時候把他們整理一下了。那是因為收藏夾太亂,橡皮擦給設置私密了,不收拾不好看呀。 ...
摘要:年前無心工作,上班刷知乎發現一篇分享爬蟲的文章。另外攜帶的數據是用來告訴服務器當前請求是從哪個頁面請求過來的。 年前無心工作,上班刷知乎發現一篇分享python爬蟲的文章。 感覺他爬取的網站里的妹子都好好看哦,超喜歡這里的,里面個個都是美女。 無小意丶:自我發掘爬蟲實戰1:宅男女神網妹子圖片批量抓取,分類保存到本地和MongoDB數據庫 無奈python雖然入門過但太久沒用早已荒廢,最...
?????? ???Hello,大家好我叫是Dream呀,一個有趣的Python博主,小白一枚,多多關照??? ???CSDN Python領域新星創作者,大二在讀,歡迎大家找我合作學習 ?入門須知:這片樂園從不缺乏天才,努力才是你的最終入場券!??? ?最后,愿我們都能在看不到的地方閃閃發光,一起加油進步??? ???一萬次悲傷,依然會有Dream,我一直在最溫暖的地方等你,唱的就是我!哈哈哈~...
摘要:很多人學習爬蟲的第一驅動力就是爬取各大網站的妹子圖片,比如比較有名的。最后我們只需要運行程序,即可執行爬取,程序運行命名如下完整代碼我已上傳到微信公眾號后臺,在癡海公眾號后臺回復即可獲取。本文首發于公眾號癡海,后臺回復即可獲取最新編程資源。 showImg(https://segmentfault.com/img/remote/1460000016780800); 閱讀文本大概需要 1...
閱讀 2702·2023-04-25 14:59
閱讀 889·2021-11-22 11:59
閱讀 635·2021-11-17 09:33
閱讀 2468·2021-09-27 13:34
閱讀 3898·2021-09-09 11:55
閱讀 2321·2019-08-30 15:44
閱讀 1123·2019-08-30 14:06
閱讀 1925·2019-08-29 16:55