为了方便大家理解,当时演示了整个过程:从安装Python,获取所需的库,做好所有相应的设置,编译基本爬虫代码,以及将获取的数据输出到.csv文件。这次作为第二部分,我们将学习如何从网站上抓图并将抓好的图片存到我们预先设置的路径。
了解怎么抓取文本数据,可以对资源抓取的步骤和需要调用的库和函数更熟悉,学习抓图就更容易。此外,我们将使用先前编写的部分代码来下载图片链接。最后,我们将使用Selenium和requests库进行学习。
进行图片抓取之前,请咨询法律专业人士,以确保您没有侵犯第三方权利,包括但不限于知识产权。
安装新旧库
为了从网站中抓图,我们会需要大量的库。在基础网络爬虫教程中,我们使用BeautifulSoup,Selenium和pandas收集数据并将其输出到.csv文件中。在这次的教程中,我们也会执行上述这些步骤,以导出抓取的数据(即图片网址)。
当然,仅将图片网址收集成列表是不够的。我们将使用其他几个库将网址内的内容存储到变量中,将其转换为图片对象,然后将其保存到指定的位置。我们新使用的库是Pillow和requests。
和上期一样,可以先用指令装下这些库:
pip install beautifulsoup4 selenium pandas
同时还要安装以下库:
1 | #install the Pillow library (used for image processing) |
2 | pip install Pillow |
3 | #install the requests library (used to send HTTP requests) |
4 | pip install requests |
此外,我们将使用内置库从网站下载图片,主要是将我们获取到的文件存储在指定的文件夹中。
定义函数
1 | import pandas as pd |
2 | from bs4 import BeautifulSoup |
3 | from selenium import webdriver |
4 | driver= webdriver.Chrome(executable_path=’/nix/path/to/webdriver/executable’) |
5 | driver.get(‘https://your.url/here?yes=brilliant’) |
6 | results= [] |
7 | content= driver.page_source |
8 | soup=BeautifulSoup(content) |
数据提取过程几乎完全相同(根据需要导入库)。我们使用webdriver选择待抓取图片链接,并创建一个列表来存储它们。当ChromeDriver进入到对应网址时,我们使用变量‘content’指向页面源,然后使用BeautifulSoup对页面源进行操作。
在上一教程中,我们使用内置和库定义的函数执行了所有操作。尽管我们可以在不定义任何函数的情况下进行另一篇教程,但对于几乎所有项目而言,这些函数都是极为有用的工具:
1 | # Example on how to define a function and select custom arguments for the |
2 | # code that goes into it. |
3 | def function_name(arguments): |
4 | # Function body goes here. |
我们将把URL爬虫移到定义的函数中。此外,我们将重复使用“ Python Web Scraping教程:循序渐进 ”一文中使用的相同代码,并将其重新用于爬取完整的URL。
之前
1 | for a in soup.findAll(attrs={‘class’: ‘class’}): |
2 | name = a.find(‘a’) |
3 | if name notin results: |
4 | results.append(name.text) |
之后
1 | #picking a name that represents the functions will be useful later on. |
2 | def parse_image_urls(classes, location, source): |
3 | for a in soup.findAll(attrs={‘class’: classes}): |
4 | name = a.find(location) |
5 | if name notin results: |
6 | results.append(name.get(source)) |
注意,我们现在以不同的方式来append指定内容。使用函数“get()”并向其中添加一个新参数“ source”,嵌套在append里, 以替代直接append文本的方法。我们使用“source”来指向网站中存储图片链接的字段,这些字段大概率会在“ src”,“ data-src”或其他类似的HTML标签中。
调用函数
假定目标网址的图片链接在类’blog-card__link’,’img’中,并且网址本身位于’src’属性中。我们将这样调用新定义的函数:
parse_image_urls(“blog-card__link”,”img”, “src”)
现在,我们的代码应如下所示:
1 | import pandas as pd |
2 | from bs4 import BeautifulSoup |
3 | from selenium import webdriver |
4 | |
5 | driver= webdriver.Chrome(executable_path=’/nix/path/to/webdriver/executable’) |
6 | driver.get(‘https://your.url/here?yes=brilliant’) |
7 | results= [] |
8 | content= driver.page_source |
9 | soup=BeautifulSoup(content) |
10 | |
11 | |
12 | def parse_image_urls(classes, location, source): |
13 | for a in soup.findAll(attrs={‘class’: classes}): |
14 | name = a.find(location) |
15 | if name notin results: |
16 | results.append(name.get(source)) |
17 | |
18 | parse_image_urls(“blog-card__link”, “img”, “src”) |
由于我们有时要导出抓取的数据且我们之前已经使用过pandas,因此可以通过将所有内容输出到“ .csv”文件中进行检查。如果需要,我们可以通过这种方式检查任何可能的语义错误。
df= pd.DataFrame(“links”: results})
df.to_csv(‘links.csv’,index=False, encoding=’utf-8′)
如果我们现在运行代码,则会在正在运行的目录中生成一个“links.csv”文件。
图片提取
假设上一节里没有遇到任何问题,我们就可以继续到下一步,从网站上下载图片。
1 | #import library requests to send HTTP requests |
2 | import requests |
3 | for b in results: |
4 | #add the content of the url to a variable |
5 | image_content = requests.get(b).content |
我们将使用请求库来获取存储在图片URL中的内容。上面的“for”循环将遍历“结果”列表。
#io manages file-related in/out operations
import io
#creates a byte object out of image_content and point the variable image_file to it
image_file= io.BytesIO(image_content)
到目前为止,还不算完,因为我们上面的“image”还只是一个Python对象。所以需要通过指令进行转换。
#we use Pillow to convert our object to an RGB image
from PIL import Image
image= Image.open(image_file).convert(‘RGB’)
转换成RGB模式的图后,还没有结束,因为我们需要设置一个保存图片的地方。就本教程而言,建一个文件夹“Test”是最简单的方法。
1 | #pathlib let’s us point to specific locations. Will be used to save our images. |
2 | import pathlib |
3 | #hashlib allows us to get hashes. We will be using sha1 to name our images. |
4 | import hashlib |
5 | #sets a file_path variable which is pointed to |
6 | #our directory and creates a file based on #the sha1 hash of ‘image_content’ |
7 | #and uses .hexdigest to convert it into a string. |
8 | file_path= pathlib.Path(‘nix/path/to/test’, hashlib.sha1(image_content).hexdigest()[:10] +’.png’) |
9 | image.save(file_path, “PNG”, quality=80) |
执行
话不多说,让我们结合前面的所有步骤,运行看看。请注意,由于我们未将数据提取到任何表中,因此此时pandas还显示为灰色。为了方便起见,我们将其保留。如果您需要查看或复查输出结果,可以直接使用。
1 | import hashlib |
2 | import io |
3 | from pathlib importPath |
4 | import pandas as pd |
5 | import requests |
6 | from bs4 importBeautifulSoup |
7 | from PIL import Image |
8 | from selenium import webdriver |
9 | |
10 | driver= webdriver.Chrome(executable_path=’/nix/path/to/webdriver/executable’) |
11 | driver.get(‘https://your.url/here?yes=brilliant’) |
12 | driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) |
13 | results= [] |
14 | content= driver.page_source |
15 | soup=BeautifulSoup(content) |
16 | |
17 | |
18 | def gets_url(classes, location, source): |
19 | results = [] |
20 | for a in soup.findAll(attrs={‘class’: classes}): |
21 | name = a.find(location) |
22 | if name notin results: |
23 | results.append(name.get(source)) |
24 | return results |
25 | |
26 | |
27 | driver.quit() |
28 | |
29 | if __name__ ==”__main__”: |
30 | returned_results =gets_url(“blog-card__link”, “img”, “src”) |
31 | for b in returned_results:: |
32 | image_content = requests.get(b).content |
33 | image_file = io.BytesIO(image_content) |
34 | image =Image.open(image_file).convert(‘RGB’) |
35 | file_path = pathlib.Path(‘nix/path/to/test’, hashlib.sha1(image_content).hexdigest()[:10] +’.png’) |
36 | image.save(file_path, “PNG”, quality=80) |
为了提高效率,在检索完所需的URL列表后,可以使用“driver.quit()”退出webdriver。我们不再需要webdriver,因为所有内容都已经存储到了本地。
运行程序将输出以下两个结果之一:
- 通过定义“file_path”变量,将图片输出到我们选择的文件夹中。
- Python输出403 HTTP访问报错。
很显然,获得第一个结果就意味着我们已经完成。当然如果我们爬取我们自己的博客页面,我们将收到第二个结果。大多数情况下,要让第二个结果变成第一个结果将花费一些时间,而且有时甚至可能会出现更困难的情况。
每当我们使用请求库将请求发送到目标服务器时,都会分配给我们一个默认的user-agent“Python-urllib / version.number”。某些网络服务商可能会特地去阻止这些user-agent,因为它们肯定是机器人。幸运的是,请求库使我们能够分配所需的任何user-agent(或整个header):
image_content= requests.get(b, headers={‘User-agent’: ‘Mozilla/5.0’}).content
在大多数情况下,添加user-agent就足够了。也有更复杂的情况,服务器可能会尝试检查HTTP header文件的其他部分以确认它是真实用户。
清理代码虽然任务已完成,但是代码仍然很混乱。通过将所有内容置于已定义的函数下,可以使我们的程序更具可读性,也方便二次调用:
1 | import io |
2 | import pathlib |
3 | import hashlib |
4 | import pandas as pd |
5 | import requests |
6 | from bs4 importBeautifulSoup |
7 | from PIL import Image |
8 | from selenium import webdriver |
9 | |
10 | |
11 | def get_content_from_url(url): |
12 | driver = webdriver.Chrome() # add “executable_path=” if driver not in running directory |
13 | driver.get(url) |
14 | driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) |
15 | page_content = driver.page_source |
16 | driver.quit() # We do not need the browser instance for further steps. |
17 | return page_content |
18 | |
19 | |
20 | def parse_image_urls(content, classes, location, source): |
21 | soup =BeautifulSoup(content) |
22 | results = [] |
23 | for a in soup.findAll(attrs={“class”: classes}): |
24 | name = a.find(location) |
25 | if name notin results: |
26 | results.append(name.get(source)) |
27 | return results |
28 | |
29 | |
30 | def save_urls_to_csv(image_urls): |
31 | df = pd.DataFrame({“links”: image_urls}) |
32 | df.to_csv(“links.csv”, index=False, encoding=”utf-8″) |
33 | |
34 | |
35 | def get_and_save_image_to_file(image_url, output_dir): |
36 | response = requests.get(image_url, headers={“User-agent”: “Mozilla/5.0”}) |
37 | image_content = response.content |
38 | image_file = io.BytesIO(image_content) |
39 | image =Image.open(image_file).convert(“RGB”) |
40 | filename = hashlib.sha1(image_content).hexdigest()[:10] +”.png” |
41 | file_path = output_dir / filename |
42 | image.save(file_path, “PNG”, quality=80) |
43 | |
44 | |
45 | def main(): |
46 | url =”https://your.url/here?yes=brilliant” |
47 | content =get_content_from_url(url) |
48 | image_urls =parse_image_urls( |
49 | content=content, classes=”blog-card__link”, location=”img”, source=”src”, |
50 | ) |
51 | save_urls_to_csv(image_urls) |
52 | |
53 | for image_url in image_urls: |
54 | get_and_save_image_to_file( |
55 | image_url, output_dir=pathlib.Path(“nix/path/to/test”), |
56 | ) |
57 | |
58 | |
59 | if __name__ ==”__main__”: #only executes if imported as main file |
60 | main() |
现在,所有内容都嵌套在明确定义的函数下,并且可以在导入时调用。这样比之前更方便。
总结
通过使用上面概述的代码,您现在应该能够完成基本的图片抓取任务了,例如一次性从网站上下载所有图片。可以通过多种方式来升级图片抓取器,其中大多数方法已在上一部分进行了概述。请查看我们的网站其他文章,以获取有关数据采集如何入门或如何高效采集数据的更多详细信息。