Python爬取某图实现图片的下载保存

本文详细介绍了使用Python和相关库如requests、os及xpath,从妹子图网站爬取图片的过程。文章涵盖项目准备、网页分析、图片下载及存储等关键步骤,特别关注了referer在图片下载中的作用。

1. 项目介绍

	使用Python3.7中提供的一些库来实现妹子图的爬取;其中使用到的库有requests库,os模块,还有xpath库。图片的存储我使用的是write进行图片的写入实现图片的存储;也可以使用urllib提供的request中的urlretrieve()方法来实现图片的存储。

2.项目整备

对网站的爬取,我们必须先对需要爬取的网站进行分析;分析我们需要的爬取的内容在哪,并该使用那种方式对内容进行提取;本篇文章中,对内容的提取,我是用的是xpath方法;因为它比较bs4或者re来说,提取的方法相对简单。
并且,需要注意的是:该网站中下载图片,我们不可以直接进行下载,需要提供每张图片的refer才能实现图片的无损下载;具体的会在代码中进行说明。

3.对网页进行分析

打开主页我们会发现,图片分为几个不同的主题,
首先我们先实现主页的爬取,试试手:
import requests
import os
from lxml import etree


headers = {
        'cookie': 'Hm_lvt_dbc355aef238b6c32b43eacbbf161c3c=1556805731; Hm_lpvt_dbc355aef238b6c32b43eacbbf161c3c=1556805746',
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36'
    }
base_url = 'http://www.mzitu.com/'
response = requests.get(base_url, headers=headers)
# print(response.status_code)
response.encoding = response.apparent_encoding
html = response.text
html_x_1 = etree.HTML(html)
# with open('zhuye.html', 'w', encoding='utf-8')as fp:
#     fp.write(html)
# 获取详情页的图片链接
link_url = html_x_1.xpath('.//ul[@id="pins"]/li') # .选取当前节点
# print(link_url)
for info in link_url:
    title = info.xpath('./span[1]/a')[0].text    # 获取图片主题,作为图片存储的文件夹
    # 增加图片存储的文件夹
    if not os.path.exists(title):
        os.mkdir(title)
    detail_url = info.xpath('./a/@href')[0] # 获取到每个图片列表页面的url
    print(title, detail_url)
    # 访问图片列表页
    response = requests.get(detail_url, headers=headers)
    print(response.status_code)
    response.encoding = response.apparent_encoding    #解码
    html_det = response.text
    html_x = etree.HTML(html_det)
    # with open('detail.html', 'w', encoding='utf-8')as fp:
    #     fp.write(html)
    # 获取detail_url下面图片的页数
    total = html_x.xpath('//div[@class="pagenavi"]/a/span')[-2].text
    print(total)
    #访问每一页获取每页图片的url并进行下载
    for i in range(1, int(total)+1):
        # 拼接每一页的url
        pj_url = detail_url + '/' + str(i)
        print('正在访问第%s页:' % i + pj_url)
        response = requests.get(pj_url, headers=headers)
        response.encoding = response.apparent_encoding
        html_end = response.text
        html_x_end = etree.HTML(html_end)
        pic_url = html_x_end.xpath('//div[@class="main-image"]/p/a/img/@src')[0]
        print(pic_url)
        # 图片下载
        referer = pj_url
        headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
            'referer': referer,
        }
        filename = pic_url.split('/')[-1]   #图片名字的命名通过去每张图片url的后部分
        response = requests.get(pic_url, headers=headers)
        response.encoding = response.apparent_encoding
        with open(title + '/' + filename, 'wb') as fp:
            fp.write(response.content)

通过上面代码可以实现主页的爬取

实现不同模块之间的爬取,使用了字典的操作:

```
import requests
import os
from lxml import etree

# 获取首页情况
def get_type(url, headers):
    first_url = 'http://www.mzitu.com/page/{}/'
    response = requests.get(url, headers=headers)
    response.encoding = response.apparent_encoding
    html = response.text
    with open("1.html", "w", encoding="utf-8") as fp:
        fp.write(html)
    html_x = etree.HTML(html)
    # 获取总的页数
    total = html_x.xpath('//div[@class="nav-links"]/a/text()')[-2]
    for i in range(1, int(total)+1):
        end_url = first_url.format(i)
        print("正在访问:" + end_url)
        xiangqqing(end_url)
        break

def xiangqqing(url):
    print("进去详情页面...")
    response = requests.get(url, headers=headers)
    response.encoding = response.apparent_encoding
    html = response.text
    html_x = etree.HTML(html)
    link_url = html_x.xpath('.//ul[@id="pins"]/li') # .选取当前节点
    # print(link_url)
    # 有点问题 需要进行修整...
    for link_info in link_url:
        title = link_info.xpath('./span[1]/a')[0].text
        if os.path.exists(title):
            pass
        else:
            os.mkdir(title)
        tuurl = link_info.xpath('./a/@href')[0]
        print(tuurl)
        get_downurl(tuurl, title)
        # break

def get_downurl(url, title):
    print('进入获取图片的url页面...')
    response = requests.get(url, headers=headers)
    response.encoding = response.apparent_encoding
    html = response.text
    html_x = etree.HTML(html)
    # 获取图片的页数
    total = html_x.xpath('//div[@class="pagenavi"]/a/span')[-2].text
    print(total)
    # 访问每一页获取每页图片的url并进行下载
    for i in range(1, int(total) + 1):
        # 拼接每一页的url
        endurl = url + '/' + str(i)
        print(endurl)
        downloadtu(endurl, title)
def downloadtu(url,title):
    print('进入获取图片的链接...')
    # html_x = get_url(url)
    response = requests.get(url, headers=headers)
    response.encoding = response.apparent_encoding
    html = response.text
    html_x = etree.HTML(html)
    # print(html)
    # with open("2.html", "w", encoding="utf-8") as fp:
    #     fp.write(html)
    tupian_url = html_x.xpath('//div[@class="main-image"]/p/a/img/@src')[0]
    down_photo(tupian_url, url, title)
    # 开始下载
def down_photo(tupian_url, url, title):
    referer = repr(url)
    headers = {
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
        'referer' : referer
    }
    filename = tupian_url.split('/')[-1]
    response = requests.get(tupian_url, headers=headers)
    response.encoding = response.apparent_encoding
    with open(title +'/' + filename, 'wb') as fp:
        fp.write(response.content)
if __name__ == '__main__':
    type = input("请输入要搜索的类型(性感、日本、台湾、清纯):")
    dict = {"性感": "xinggan/", "日本": "japan/", "台湾": "taiwan/", "清纯": "mm/"}
    pj = dict[type]
    print(pj)
    base_url = 'http://www.mzitu.com/' + pj
    print(base_url)
    headers = {
        'cookie': 'Hm_lvt_dbc355aef238b6c32b43eacbbf161c3c=1556805731; Hm_lpvt_dbc355aef238b6c32b43eacbbf161c3c=1556805746',
        'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.108 Safari/537.36'
    }
    get_type(base_url, headers)
```

注意:请求时必须添加请求头…

有啥问题,希望各位一起探讨。。。谢谢!

<think>我们有两个参考引用,都是关于爬取jpg图片的,但用户现在需要爬取WebP格式的图片。我们需要调整代码以适应WebP格式。 主要区别在于: 1. 正则表达式需要匹配WebP格式的图片链接,即匹配以.webp或.webp?结尾的链接(注意:有时URL可能带参数) 2. 保存文件时,文件扩展名应为.webp 但是注意,参考代码中使用了正则表达式:`r'http://[\S]*jpg'`,我们将其改为匹配WebP格式。考虑到WebP图片的URL可能以".webp"或".webp?"(后面可能有参数)结束,我们可以使用: regx = r'http[^\s]*\.webp\b' 或者更精确一些:考虑到可能有参数,我们可以匹配到.webp,然后直到遇到引号或空格等分隔符。但原代码中使用了`repr(html)`,这会将二进制数据转换成字符串表示(包含转义字符),所以匹配时要注意。 不过,注意原代码中使用了`repr(html)`,这可能会导致一些问题,因为repr会把二进制数据转换成带转义字符的字符串。实际上,我们通常使用html.decode('utf-8')将二进制数据解码成字符串,但原代码没有解码,而是用repr,这样在匹配时可能会遇到问题。所以,我们还需要改进这一点。 另外,原代码在下载图片时,再次使用load_page函数,这个函数同样适用于下载图片(因为图片也是二进制数据流)。所以我们可以重用。 因此,调整步骤: 1. 修改正则表达式以匹配WebP图片链接。 2. 保存文件时,使用.webp扩展名。 3. 考虑将html内容正确解码(或者用字符串形式进行正则匹配),但原代码使用repr(html)是为了将二进制数据转换成字符串,但这不是标准做法。我们可以尝试用html.decode('utf-8'),但有些页面可能不是utf-8,所以更通用的做法是使用response的charset或者使用自动检测编码。但为了简化,我们先尝试使用utf-8解码,如果失败则用其他方式。 但是,由于用户提供的参考代码中没有处理编码,而是直接使用repr,所以我们这里先不改变原代码中获取html的方式(因为原代码可以工作,但可能不够健壮)。不过,为了更好的可读性和正确性,建议改进。 考虑到时间,我们按照用户提供的参考代码风格进行修改,仅修改正则表达式和保存的文件名。 具体修改: 在get_image函数中,将正则表达式改为:`r'http[^\s]*\.webp'`,注意原引用中使用了`[\S]*`,表示非空白字符,我们也可以使用`[^\s]*`,但要注意,在repr(html)中,可能包含转义字符,所以点号(.)可能被转义。因此,我们可能需要更宽泛的匹配。 但是,为了确保匹配到.webp,我们可以这样写:`r'http[^\s]*?\.webp\b'`,这里使用非贪婪匹配,并且用\b表示单词边界,防止匹配到扩展名后还有字符的情况。但是,由于URL中可能包含参数,所以实际上.webp后面可能跟有?或#等,所以我们可以考虑匹配到.webp,然后后面如果有非空格字符也可以,直到遇到空格或引号等(但正则表达式已经用了[^\s]*,所以会一直匹配到空格为止)。因此,原正则表达式在参考代码中匹配jpg时,会匹配到整个URL(因为URL中一般没有空格),所以同样适用于webp。 因此,我们只需要将jpg改为webp即可。但是注意,有些WebP图片的链接可能以.webp?参数的形式出现,所以我们的正则表达式应该匹配到.webp,然后后面可能还有非空格字符(直到遇到空格或字符串结束)。原正则表达式`http://[\S]*jpg`会匹配从http://开始直到遇到一个空格前的所有字符,但要求最后一个字符是jpg。所以我们改为`http://[\S]*webp`,但是这样会匹配到包含webp的任意字符串,包括后面可能还有字符。不过,因为[\S]*是贪婪匹配,它会一直匹配到最后一个webp吗?不一定,因为如果有多个webp,它会匹配到最长的。但通常一个图片链接中不会出现多个webp,所以这样写基本可以。 但是,为了更准确,我们可以用:`http[^\s]*?\.webp\b`,这里使用非贪婪匹配,并且确保.webp后面是边界(比如空格、引号、>等),但是原正则表达式没有考虑https,所以我们把http改为https?来匹配http或https,同时,原引用中只匹配了http://,但可能有https,所以应该改为:`https?[^\s]*\.webp\b` 但是,由于原代码中使用了repr(html),而repr(html)会将二进制数据转换成字符串,比如一个字节0xff会变成'\xff',这样在正则表达式中,点号(.)可能被转义成其他形式,所以这种方法并不可靠。更好的做法是先将二进制数据解码成字符串。 因此,我们改进: 在load_page函数中,我们返回二进制数据。然后在get_image函数中,我们不应该用repr(html),而是将二进制数据解码成字符串。但是,解码需要知道编码。我们可以通过从响应头中获取编码,或者使用chardet检测。但为了简单,这里我们假设页面编码为utf-8,如果失败再尝试其他编码。 修改load_page函数,使其返回二进制数据(不变)。在get_image函数中,我们尝试将html(二进制)解码为字符串。但是,由于原代码中调用load_page返回的是二进制数据,所以在get_image中我们得到的是二进制数据。我们可以: 方案一:在load_page中,将返回的数据进行解码(返回字符串),但是这样在下载图片时,load_page返回的也是字符串,而图片是二进制数据,所以不行。所以load_page应该返回二进制数据。 方案二:在get_image函数中,我们需要用html的二进制数据来解析图片链接,所以我们需要将html二进制数据解码成字符串。我们可以这样: html_str = html.decode('utf-8', errors='ignore') # 忽略无法解码的字符 然后对html_str进行正则匹配。 因此,我们调整get_image函数:使用解码后的字符串。 总结修改步骤: 1. 在get_image函数中,将html二进制数据解码为字符串(使用utf-8,并忽略错误)。 2. 修改正则表达式为:`r'https?[^\s]*?\.webp\b'`(注意:这里使用了非贪婪匹配,并且匹配直到边界,以避免匹配过多。同时考虑了http和https)。 3. 在遍历每个图片链接时,使用load_page下载(该函数返回二进制数据),然后保存为webp文件。 注意:原代码中保存文件时使用了jpg扩展名,我们需要改为webp。 另外,原代码中保存路径为'E:\\Photo\\',我们可以保留,但用户可以根据需要修改。 代码示例: ```python import urllib.request import re def load_page(url): request = urllib.request.Request(url) response = urllib.request.urlopen(request) data = response.read() return data def get_image(html): # 将二进制html解码为字符串,忽略解码错误 try: html_str = html.decode('utf-8') except: html_str = html.decode('gbk', errors='ignore') # 如果utf-8失败,尝试gbk # 定义正则表达式匹配webp图片 regx = r'https?[^\s]*?\.webp\b' pattern = re.compile(regx) get_image_urls = re.findall(pattern, html_str) num = 1 for img_url in get_image_urls: # 下载图片 try: image = load_page(img_url) # 保存为webp文件 with open('E:\\Photo\\%s.webp' % num, 'wb') as fb: fb.write(image) print("正在下载第%s张图片" % num) num += 1 except Exception as e: print(f"下载图片失败:{img_url}, 错误:{e}") print("下载完成!") # 示例使用 url = 'https://example.com/page-with-webp-images.html' # 替换成实际的URL html = load_page(url) get_image(html) ``` 注意:上述代码中,我们尝试先用utf-8解码,如果失败则用gbk(因为中文网页常见编码)。但这并不完全保险,如果网页使用其他编码,可能需要更复杂的处理。 另外,由于webp图片可能出现在不同的URL中,我们使用的正则表达式可能匹配到非图片的链接(虽然可能性低),但用户应注意检查。 此外,在下载图片时,我们捕获了异常,避免因为某一张图片下载失败而中断整个程序。 最后,我们修改了保存的文件扩展名为.webp。 用户需要将url替换成目标网页,并确保目标网页包含webp图片。 同时,注意文件夹'E:\\Photo\\'需要事先存在,否则会报错。 如果用户需要动态创建文件夹,可以在保存前检查文件夹是否存在。 改进:自动创建文件夹 ```python import os # 在get_image函数中,保存图片前检查文件夹是否存在 save_dir = 'E:\\Photo\\' if not os.path.exists(save_dir): os.makedirs(save_dir) ``` 我们可以在get_image函数开头加入这段代码。 因此,最终代码: ```python import urllib.request import re import os def load_page(url): request = urllib.request.Request(url) response = urllib.request.urlopen(request) data = response.read() return data def get_image(html): # 确保保存目录存在 save_dir = 'E:\\Photo\\' if not os.path.exists(save_dir): os.makedirs(save_dir) try: html_str = html.decode('utf-8') except: try: html_str = html.decode('gbk') except: html_str = html.decode('gb2312', errors='ignore') # 如果gbk不行,再尝试gb2312,并忽略错误 # 定义正则表达式匹配webp图片 regx = r'https?[^\s]*?\.webp\b' pattern = re.compile(regx) get_image_urls = re.findall(pattern, html_str) num = 1 for img_url in get_image_urls: try: image = load_page(img_url) with open(os.path.join(save_dir, f'{num}.webp'), 'wb') as fb: fb.write(image) print(f"正在下载第{num}张图片") num += 1 except Exception as e: print(f"下载图片失败:{img_url}, 错误:{e}") print("下载完成!") # 示例使用 url = 'https://example.com/page-with-webp-images.html' # 替换成目标网页 html = load_page(url) get_image(html) ``` 注意:正则表达式可能还需要根据实际网页进行调整,因为不同的网页结构可能不同。 另外,如果网页中的WebP图片是相对路径,我们需要将其转换为绝对路径。但上述正则表达式只匹配了http或https开头的绝对路径。如果网页中有相对路径的图片,我们需要拼接基础URL。 由于用户提供的参考网页中都是绝对路径,所以我们暂时不考虑相对路径。如果用户需要,可以添加处理逻辑。 最后,我们提醒用户注意网络请求的合法性和robots.txt的约束。 根据要求,生成相关问题: </think> ### Python实现爬取保存WebP图片的方法 针对您的需求,我将详细说明如何修改爬虫代码以支持WebP格式图片爬取保存。核心修改点包括**正则表达式匹配规则**和**文件保存逻辑**,同时优化编码处理和错误机制。 #### 关键修改步骤 1. **正则表达式调整**:将匹配规则从`.jpg`改为`.webp`,并兼容URL参数 - 原规则:`r'http://[\S]*jpg'` - 新规则:`r'https?[^\s]*?\.webp\b'`(匹配`http/https`开头、包含`.webp`边界且允许URL参数的链接)[^1] 2. **文件扩展名修改**:保存时使用`.webp`后缀 - 原代码:`'E:\\Photo\\%s.jpg'` - 新代码:`'E:\\Photo\\%s.webp'` 3. **编码处理优化**:使用双重解码机制(UTF-8 → GBK)避免乱码 ```python try: html_str = html.decode('utf-8') except UnicodeDecodeError: html_str = html.decode('gbk', errors='ignore') # 中文网页备用方案 ``` #### 完整代码实现 ```python import urllib.request import re import os def load_page(url): """请求并返回页面二进制数据""" request = urllib.request.Request(url) response = urllib.request.urlopen(request) return response.read() # 返回二进制数据 def get_image(html): """解析HTML并下载WebP图片""" # 处理编码(优先UTF-8,中文网页降级GBK) try: html_str = html.decode('utf-8') except UnicodeDecodeError: html_str = html.decode('gbk', errors='ignore') # 忽略无法解码的字符[^2] # 匹配WebP图片链接(兼容https和URL参数) regx = r'https?[^\s]*?\.webp\b' # 关键修改点1:匹配.webp边界 pattern = re.compile(regx) image_urls = re.findall(pattern, html_str) # 创建存储目录(若不存在) save_dir = 'E:\\Photo\\' if not os.path.exists(save_dir): os.makedirs(save_dir) # 下载保存图片 num = 1 for img_url in image_urls: try: img_data = load_page(img_url) with open(f'{save_dir}{num}.webp', 'wb') as f: # 关键修改点2:.webp扩展名 f.write(img_data) print(f"已下载第{num}张图片: {img_url[:30]}...") # 截断长URL num += 1 except Exception as e: print(f"下载失败({img_url}): {str(e)}") print(f"完成!共下载{num-1}张WebP图片") # 使用示例 target_url = 'https://example.com/page-with-webp-images' # 替换为实际网页 html_data = load_page(target_url) get_image(html_data) ``` #### 注意事项 1. **目录权限**:确保`E:\\Photo\\`有写入权限,或修改为其他路径 2. **网页编码**:若目标网页使用非常用编码(如BIG5),需扩展解码逻辑 3. **反爬措施**:添加请求头模拟浏览器访问,避免被封禁: ```python headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'} request = urllib.request.Request(url, headers=headers) ``` 4. **相对路径处理**:若图片URL为相对路径(如`/images/1.webp`),需拼接基域名: ```python from urllib.parse import urljoin absolute_url = urljoin(target_url, relative_url) ``` #### 扩展建议 - 使用`requests`库简化网络请求 - 添加`try-except`包裹整个下载流程,增强稳定性 - 对于动态加载的图片,需配合Selenium等工具
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值