好友
阅读权限 10
听众
最后登录 1970-1-1
本帖最后由 wty1641 于 2019-12-4 17:41 编辑
谁年少没有个漫画梦,犹记当初,网络还没有现在这么方便,想看漫画只能节衣缩食,攒到 5 毛钱租一本漫画,就可以在幽暗的个人漫画租阅室里就能度过一个快乐的下午,现在想起来真是回忆满满。 现在有了网络,看漫变成了 一件不再困难的事,但是最近常去的漫画网突然关停,让我意识到网上有的东西,还是放在硬盘里最安全。但是漫画一张一张另存为图片,不是我们好青年所为,于是乎,就以一代人的记忆漫画《火影忍者》为例,编写了一个下载爬虫,实现自动下载并按章节自动建立文件夹的功能,在此向《风之动漫》网站致敬。 本文仅限于交流学习! 首先我们打开网页,进入到《火影忍者》漫画目录页,如图 1-1:
图1-1 漫画目录页
图 1-1 漫画目录页
总体来说,本漫画的爬虫,分为两个部分:
一是对目录页的爬取,获得所有详情页的跳转链接;
二是进入详情页后,对其中每一页的图片进行保存。
说干就干。 要实现对目录页的爬取,我们需要用到的 requests及 lxml库,那么为了方便,在这里我把该爬虫所需要的所有库都列出来。
一、导入库:
[Python] 纯文本查看 复制代码
#第一部分目录页解析主要使用库:
import requests
from lxml import etree
#第二部分详情页解析主要使用库:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
#其他辅助库
import time
import os
import re
二、实现目录页面爬取:
# 要爬取的网页网址
[Python] 纯文本查看 复制代码
URL= 'https://manhua.fzdm.com/1/'
# 建立用于伪装的header:包括User-Agent、referer和cookie。 这一部分在浏览器打开网页后— F12— F5刷新— Network— name中读取的网址(一般来说是第一项)— Headers选项—— Request Headers中。加入 headers是最为基础的掩盖爬虫手段的方法,根据网站反爬虫机制的程度,决定添加项目的多少,一般来说有此三项足够。如图 2-1。
图2-1 Request Headrest样式
图2-1 Request Headrest样式
[Python] 纯文本查看 复制代码
[size=4]HEADER = {[/size][/align][size=4]
[align=left]
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',[/align]
[align=left]
'referer': 'https://www.fzdm.com/',[/align]
[align=left]
'cookie': '_ga=GA1.1.2099399557.1574420824; picHost=www-mipengine-org.mipcdn.com/i/p1.manhuapan.com; _ga_1FZE0C2L80=GS1.1.1574833839.2.1.1574833854.0'[/align]
[align=left]
[size=4]}[/size][size=14px]
[Python] 纯文本查看 复制代码
#建立 分析目录页,提取详情页的函数parse_catalogue(),这里需要给定参数url,
def parse_catalogue(url):
#调用requests库中的get命令,获取要爬取的目录页内容
req = requests.get(url=url, headers=HEADER)
#通过lxml库中etree.HTML将解析字符串格式的HTML文档对象,注意:使用req.content获取的是网页的bytes型也就是二进制的数据。
html = etree.HTML(req.content)
#在这里我们通过分析网页源代码,使用 xpath命令进行定位,可以看出其中的 href就是我们的要获得的网页地址(……的一部分)。如图 2-2。
目录页源码
[Python] 纯文本查看 复制代码
href = html.xpath("//div[@id='content']/li/a/@href")[/align][align=left]
#在我们得到了所有的href后,通过for循环将其取出,通过字符串的组合我们我们就能得到详情页的真是网址,即content_url。例:“https://manhua.fzdm.com/1/ + brc25/ ”[/align]
[align=left]
for x in href:[/align]
[align=left]
content_url = URL + x[/align][align=left]
#打印组合完成的新网址,这样在爬取的时候,我们就可以看到下载到了哪里。[/align]
[align=left]
print(content_url)[/align][align=left]
#将content_url得到的网址传入到parse_content函数,这里使用的parse_content函数是下面要建立的详情页解析函数。[/align]
[align=left]
parse_content(content_url)[/align][align=left]
#使用time函数,每次解析一个网页后停滞2秒。[/align]
[align=left]
time.sleep(2)
到这里,我们完成了爬虫功能的一半,实现了对所有详情页的网址的获取及将详情页网址导入详情页解析函数的操作。 这样我们在启动爬虫的的时候,只需要调用一个函数( parse_catalogue )就能实现完整解析下载功能。
三、详情页解析,下载漫画
那么我们接下来就要实现对详情页的处理了。在这里我们同样建立一个函数,就是我们刚刚提到的 parse_content函数,从而实现对详情页解析,并正式下载漫画。 进入到详情页中,我们首先来对详情页进行分析,这里该网站采用的是一个页面显示一张漫画的方法,想看下一页漫画,只能通过点击下一页或者点击可见到的数字来跳转到相关页面。如图 3-1.。
图3-1 详情页跳转方式
同样在源码中,我们也可以看到,该网页只呈现了当前网页的中可见的少量漫画页面跳转地址,并没有呈现出该回漫画总共的页面数。图3-2.
图3-2 详情页跳转方式源码
图 3-2 详情页跳转方式源码
因此,我这里采用 selenium+chromedriver模拟 Chrome浏览器的方式进行让它不断点击下一页,从而实现网页的跳转。 开始建立 selenium的 driver: [Python] 纯文本查看 复制代码
#chromedriver的地址,根据个人不同进行修改。[/size][/align][/size]
[align=left]
Driver_path = r'C:/Chrome/chromedriver.exe'[/align][align=left]
#设置ChromeOptions,实现不显示页面自动执行。[/align]
[align=left]
Option = webdriver.ChromeOptions()[/align][align=left]
Option.add_argument('headless')[/align]
[align=left]
#导入设置,建立Driver。[/align][align=left]
Driver = webdriver.Chrome(Driver_path, options=Option) 建立 parse_content函数,这里的 url需要把从 parse_ catalogueh 函数中解析组合得到的 url(也就是真正的详情页地址)传入进来。 [Python] 纯文本查看 复制代码
def parse_content(url):[/align][align=left]
#使用Driver.get打开详情页[/align]
[align=left]
Driver.get(url)[/align]
[align=left]
#获取详情页中该回漫画的名称,find_element_by_xpath是使用xpath语法的形式进行定位,注意这里虽然定位的方式使用的是xpath,但是该方法是用来定位element的,不能直接获得text,所以需要在获取到的element最后使用“.text”方法获得其中文本内容。[/align][align=left]
title = Driver.find_element_by_xpath('//div[@id="pjax-container"]/h1').text[/align][align=left]
#打印标题[/align]
[align=left]
print(title)[/align]
[align=left]
#使用os函数建立多层目录,我把目录建立都在e盘的comic文件夹下,按照漫画章节标题单独建立目录。[/align][align=left]
if os.path.isdir('e:/comic/' + title):[/align][align=left]
pass[/align][align=left]
else:[/align][align=left]
os.makedirs('e:/comic/' + title)[/align][align=left]
#因为在网页中,我们没有办法一目了然地看到该回漫画总计页数,所以在这里使用while True循环,让他不断遍历,直到达到相应的条件,则终止循环。[/align][align=left]
while True:[/align]
[align=left]
#显式等待,每次进入新网页img标签出现再进行下一步[/align][align=left]
WebDriverWait(Driver, 10).until(ec.presence_of_element_located((By.XPATH, "//div[@id='mhimg0']//img")))[/align]
[align=left]
#获取每一个页面中,漫画图片的真实地址。[/align][align=left]
img = Driver.find_element_by_xpath("//div[@id='mhimg0']//img")[/align][align=left]
src = img.get_attribute("src")[/align]
[align=left]
#在这里,获得的src中的网址,不是真正的漫画图片地址,例:“http://www-mipengine-org.mipcdn.com/i/p1.manhuapan.com/2018/12/17021845640584 .jpg”,真实的地址是“p1.manhuapan.com/2018/12/17021845640584.jpg”,这里使用正则表达式就可以取出来,再拼合头部就可以得到真正的漫画图片地址。[/align]
[align=left]
顺便说一句,如此清新脱俗的反爬,让我感动不已,天知道我第一次下载时看到一文件夹打不开的jpg是什么表情。[/align][align=left]
src1 = 'http://' + re.search('\w\d.+.jpg', src).group()[/align][align=left]
print(src1)[/align]
[align=left]
#使用requests.get请求图片的网址[/align][align=left]
comic_content = requests.get(url=src1, headers=HEADER)[/align][align=left]
#将图片保存到对应文件夹中,文件名按照下载的图片名定义[/align][align=left]
with open('e:/comic/' + title + '/' + re.search('\w+.jpg', src).group(), 'wb') as f:[/align][align=left]
f.write(comic_content.content)
#这里,需要注意: 该网站共每一次打开新的漫画页面的时候,右下角都会出现一个弹窗的广告,这个弹窗广告的位置刚好遮住了下一页的位置,所以使用selenium时需要先关闭这个广告,才能点击下一页。关闭的按键在源代码里显示在<ahref=’ javascript:void();’>中,如图3-3.
图3-3 广告关闭按键的源码
这种形式的element不能直接进行定位然后单击,只有使用find_elements取出所有的a标签,然后for循环配合条件语句进行查找,实现点击。 [Python] 纯文本查看 复制代码
#等待a标签的出现。[/font][/align][/font][align=left]
WebDriverWait(Driver, 10).until(ec.presence_of_element_located((By.TAG_NAME, 'a')))[/align]
[align=left]
#定位全部的a标签,根据条件查找对应a标签,进行点击。[/align]
[align=left]
a = Driver.find_elements_by_tag_name('a')[/align][align=left]
for x in a:[/align][align=left]
if 'javascript:void()' in x.get_attribute("href"):[/align][align=left]
x.click()
[Python] 纯文本查看 复制代码
#当该回漫画到最后一页,会在网页最后出现“最后一页了”的字样,在这里当出现最后一页了的字样后,循环停止跳出,否则只要出现“下一页”字样,就重复循环。[/align][align=left]
end_story = ec.text_to_be_present_in_element((By.XPATH, '//div[@class="navigation"]'), "最后一页了")(Driver)[/align][align=left]
if end_story is True:[/align][align=left]
break[/align][align=left]
else:[/align][align=left]
next_page = Driver.find_element_by_link_text('下一页')[/align][align=left]
WebDriverWait(Driver, 10).until([/align][align=left]
ec.presence_of_element_located((By.XPATH, '//a[@class="pure-button pure-button-primary"]')))[/align][align=left]
next_page.click()
爬取结果如图3-4,图3-5。
图3-5漫画内容下载样例
四、结语:
这是一个非常简单的爬虫,但其实用性和通用性均比较强(比如,更换漫画的url可以获取该网站所有漫画),对于其他网站的图片批量获取也有着借鉴意义。本代码开源,本文也仅供技术交流,不承担其他人使用责任,望大家秉中正之心,持守正之器。在这里还是要感谢《风之动漫》网站,给了我和很多喜爱漫画的同好们一个休闲的地方,望越做越好。 突发奇想,如果把爬下来的漫画打印出来,然后开一个小时候的漫画出租屋,是不是很快就可以走上人生巅峰了啊。哈哈。 (首发知乎,欢迎关注! 第一次发帖有不对的地方请管理员告知! ) 码字码码都不易,喜欢的评个分点个赞呀!
代码和网页上一致,单独整理出来,以供参考。
免费评分
查看全部评分
发帖前要善用【论坛搜索 】 功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。