博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python爬虫入门教程 5-100 27270图片爬取
阅读量:6848 次
发布时间:2019-06-26

本文共 11970 字,大约阅读时间需要 39 分钟。

获取待爬取页面

今天继续爬取一个网站,http://www.27270.com/ent/meinvtupian/ 这个网站具备反爬,so我们下载的代码有些地方处理的也不是很到位,大家重点学习思路,有啥建议可以在评论的地方跟我说说。

为了以后的网络请求操作方向,我们这次简单的进行一些代码的封装操作。

在这里你可以先去安装一个叫做 retrying 的模块

pip install retrying复制代码

这个模块的具体使用,自己去百度吧。嘿嘿哒~

在这里我使用了一个随机产生user_agent的方法

import requestsfrom retrying import retryimport randomimport datetimeclass R:    def __init__(self,method="get",params=None,headers=None,cookies=None):		# do something    def get_headers(self):        user_agent_list = [ \            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1" \            "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", \            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6", \            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6", \            "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1", \            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5", \            "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5", \            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \            "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3", \            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3", \            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \            "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3", \            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3", \            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24", \            "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"        ]        UserAgent = random.choice(user_agent_list)        headers = {
'User-Agent': UserAgent} return headers #other code复制代码

retrying 最简单的使用就是给你想不断重试的方法加上 装饰器 @retry

在这里,我希望网络请求模块尝试3次之后,在报错!

同时在R类初始化方法中增加一些必备的参数,你可以直接看下面的代码

__retrying_requests 方法为私有方法,其中根据getpost方式进行逻辑判断

import requestsfrom retrying import retryimport randomimport datetimeclass R:    def __init__(self,method="get",params=None,headers=None,cookies=None):		#do something    def get_headers(self):        # do something    @retry(stop_max_attempt_number=3)    def __retrying_requests(self,url):        if self.__method == "get":            response = requests.get(url,headers=self.__headers,cookies=self.__cookies,timeout=3)        else:            response = requests.post(url,params=self.__params,headers=self.__headers,cookies=self.__cookies,timeout=3)        return response.content   	# other code复制代码

网络请求的方法已经声明完毕,并且返回 response.content 数据流

下面基于这个私有方法,增加一个获取网络文本的方法和一个获取网络文件的方法。同步完善类的初始化方法,在开发中发现,我们要爬取的网页编码是gb2312 所以还需要给某些方法增加一个编码参数

import requestsfrom retrying import retryimport randomimport datetimeclass R:	# 类的初始化方法    def __init__(self,method="get",params=None,headers=None,cookies=None):        self.__method = method        myheaders = self.get_headers()        if headers is not None:            myheaders.update(headers)        self.__headers = myheaders        self.__cookies = cookies        self.__params = params    def get_headers(self):       # do something    @retry(stop_max_attempt_number=3)    def __retrying_requests(self,url):		# do something    # get请求    def get_content(self,url,charset="utf-8"):        try:            html_str = self.__retrying_requests(url).decode(charset)        except:            html_str = None        return html_str    def get_file(self,file_url):        try:            file = self.__retrying_requests(file_url)        except:            file = None        return file复制代码

到此,这个R类已经被我们完善了,完整的代码,你应该从上面拼凑起来,你也可以直接翻到文章最后面,去github上直接查阅。

接下来,就是比较重要的爬虫代码部分了。这一次,我们可以简单的使用一下类和对象,并且加上简单的多线程操作。

首先,创建一个 ImageList 类,这个类第一件事情,需要获取我们爬取页面的总页码数目

这个步骤比较简单

  1. 获取网页源码
  2. 正则匹配末页元素
  3. 提取数字
import http_help as hh   # 这个http_help 是我上面写到的那个R类import reimport threadingimport timeimport osimport requests# 获取所有待爬取的URL列表class ImageList():    def __init__(self):        self.__start = "http://www.27270.com/ent/meinvtupian/list_11_{}.html"  # URL模板        # 头文件        self.__headers = {
"Referer":"http://www.27270.com/ent/meinvtupian/", "Host":"www.27270.com" } self.__res = hh.R(headers=self.__headers) # 初始化访问请求 def run(self): page_count = int(self.get_page_count()) if page_count==0: return urls = [self.__start.format(i) for i in range(1,page_count)] return urls # 正则表达式匹配末页,分析页码 def get_page_count(self): # 注意这个地方需要传入编码 content = self.__res.get_content(self.__start.format("1"),"gb2312") pattern = re.compile("
  • 末页
  • ") search_text = pattern.search(content) if search_text is not None: count = search_text.group(1) return count else: return 0if __name__ == '__main__': img = ImageList() urls = img.run()复制代码

    上面的代码注意get_page_count方法,该方法已经获取到了末尾的页码

    我们在run方法内部,通过一个列表生成器

    urls = [self.__start.format(i) for i in range(1,page_count)]复制代码

    批量把要爬取的所有链接都生成完毕。

    分析上面爬取到的URL列表,捕获详情页

    我们采用生产者和消费者模型,就是一个抓取链接图片,一个下载图片,采用多线程的方式进行操作,需要首先引入

    import threadingimport time复制代码

    完整代码如下

    import http_help as hhimport reimport threadingimport timeimport osimport requestsurls_lock = threading.Lock()  #url操作锁imgs_lock = threading.Lock()  #图片操作锁imgs_start_urls = []class Product(threading.Thread):    # 类的初始化方法    def __init__(self,urls):        threading.Thread.__init__(self)        self.__urls = urls        self.__headers = {
    "Referer":"http://www.27270.com/ent/meinvtupian/", "Host":"www.27270.com" } self.__res = hh.R(headers=self.__headers) # 链接抓取失败之后重新加入urls列表中 def add_fail_url(self,url): print("{}该URL抓取失败".format(url)) global urls_lock if urls_lock.acquire(): self.__urls.insert(0, url) urls_lock.release() # 解锁 # 线程主要方法 def run(self): print("*"*100) while True: global urls_lock,imgs_start_urls if len(self.__urls)>0: if urls_lock.acquire(): # 锁定 last_url = self.__urls.pop() # 获取urls里面最后一个url,并且删除 urls_lock.release() # 解锁 print("正在操作{}".format(last_url)) content = self.__res.get_content(last_url,"gb2312") # 页面注意编码是gb2312其他格式报错 if content is not None: html = self.get_page_list(content) if len(html) == 0: self.add_fail_url(last_url) else: if imgs_lock.acquire(): imgs_start_urls.extend(html) # 爬取到图片之后,把他放在待下载的图片列表里面 imgs_lock.release() time.sleep(5) else: self.add_fail_url(last_url) else: print("所有链接已经运行完毕") break def get_page_list(self,content): # 正则表达式 pattern = re.compile('
  • .*?
  • ') list_page = re.findall(pattern, content) return list_page复制代码

    上述代码中比较重要的有 threading.Lock() 锁的使用,在多个线程之间操作全局变量,需要进行及时的锁定; 其他的注意内容,我已经添加在注释里面,只要你按着步骤一点点的写,并且加入一些自己微妙的理解,就可以搞定。

    到现在为止,我们已经抓取到了所有的图片地址,我把他存放在了一个全局的变量里面 imgs_start_urls 那么现在又来了

    这个列表里面存放的是 http://www.27270.com/ent/meinvtupian/2018/298392.html 这样的地址,当你打开这个页面之后,你会发现只有一张图片 ,并且下面有个分页。

    点击分页之后,就知道规律了

    http://www.27270.com/ent/meinvtupian/2018/298392.html http://www.27270.com/ent/meinvtupian/2018/298392_2.html http://www.27270.com/ent/meinvtupian/2018/298392_3.html http://www.27270.com/ent/meinvtupian/2018/298392_4.html ....复制代码

    当你进行多次尝试之后,你会发现,后面的链接完全可以靠拼接完成,如果没有这个页面,那么他会显示?

    好了,如果你进行了上面的操作,你应该知道接下来怎么实现啦!

    我把所有的代码,都直接贴在下面,还是用注释的方式给大家把最重要的地方标注出来

    class Consumer(threading.Thread):	# 初始化    def __init__(self):        threading.Thread.__init__(self)        self.__headers = {
    "Referer": "http://www.27270.com/ent/meinvtupian/", "Host": "www.27270.com"} self.__res = hh.R(headers=self.__headers) # 图片下载方法 def download_img(self,filder,img_down_url,filename): file_path = "./downs/{}".format(filder) # 判断目录是否存在,存在创建 if not os.path.exists(file_path): os.mkdir(file_path) # 创建目录 if os.path.exists("./downs/{}/{}".format(filder,filename)): return else: try: # 这个地方host设置是个坑,因为图片为了防止盗链,存放在另一个服务器上面 img = requests.get(img_down_url,headers={
    "Host":"t2.hddhhn.com"},timeout=3) except Exception as e: print(e) print("{}写入图片".format(img_down_url)) try: # 图片写入不在赘述 with open("./downs/{}/{}".format(filder,filename),"wb+") as f: f.write(img.content) except Exception as e: print(e) return def run(self): while True: global imgs_start_urls,imgs_lock if len(imgs_start_urls)>0: if imgs_lock.acquire(): # 锁定 img_url = imgs_start_urls[0] #获取到链接之后 del imgs_start_urls[0] # 删掉第0项 imgs_lock.release() # 解锁 else: continue # http://www.27270.com/ent/meinvtupian/2018/295631_1.html #print("图片开始下载") img_url = img_url[0] start_index = 1 base_url = img_url[0:img_url.rindex(".")] # 字符串可以当成列表进行切片操作 while True: img_url ="{}_{}.html".format(base_url,start_index) # url拼接 content = self.__res.get_content(img_url,charset="gbk") # 这个地方获取内容,采用了gbk编码 if content is not None: pattern = re.compile('
    [\s\S.]*?img alt="(.*?)".*? src="(.*?)" />') # 匹配图片,匹配不到就代表本次操作已经完毕 img_down_url = pattern.search(content) # 获取到了图片地址 if img_down_url is not None: filder = img_down_url.group(1) img_down_url = img_down_url.group(2) filename = img_down_url[img_down_url.rindex("/")+1:] self.download_img(filder,img_down_url,filename) #下载图片 else: print("-"*100) print(content) break # 终止循环体 else: print("{}链接加载失败".format(img_url)) if imgs_lock.acquire(): # 锁定 imgs_start_urls.append(img_url) imgs_lock.release() # 解锁 start_index+=1 # 上文描述中,这个地方需要不断进行+1操作 复制代码

    所有的代码都在上面了,关键的地方我尽量加上了标注,你可以细细的看一下,实在看不明白,就多敲几遍,因为没有特别复杂的地方,好多都是逻辑。

    最后附上main部分的代码,让我们的代码跑起来

    if __name__ == '__main__':    img = ImageList()    urls = img.run()    for i in range(1,2):        p = Product(urls)        p.start()    for i in range(1,2):        c = Consumer()        c.start()复制代码

    一会过后,就慢慢收图吧

    这个版本的代码有点问题,对反爬处理的并不是很到位,大家先学习基础的,这个地方找时间,我在完善一下。

    隐藏彩蛋,重要的事情说100遍:爬虫入门,爬虫入门,爬虫入门,爬虫入门,爬虫入门,爬虫入门,爬虫入门,爬虫入门爬虫入门,爬虫入门,爬虫入门,爬虫入门,爬虫入门,爬虫入门,爬虫入门,爬虫入门

    转载地址:http://ksvul.baihongyu.com/

    你可能感兴趣的文章
    Oracle下sqlplus无法使用命令退格删除和历史记录的解决方法--使用rlwrap
    查看>>
    Centos 5.5 上面Wordpress平台搭建
    查看>>
    GNU make manual 翻译( 一百七十六)
    查看>>
    LeetCode Binary Tree Level Order Traversal 2
    查看>>
    webpack CommonsChunkPlugin详细教程
    查看>>
    Java RSA 加密 解密 签名 验签
    查看>>
    printFinal用法示例
    查看>>
    ELK系列~Nxlog日志收集加转发(解决log4日志换行导致json转换失败问题)
    查看>>
    git命令合并分支代码
    查看>>
    cookie和session
    查看>>
    cmd批处理中set /a和set /p的区别介绍
    查看>>
    Forms authentication timeout vs sessionState timeout
    查看>>
    C++虚函数表分析
    查看>>
    如何理解“哲学家们只是用不同的方式解释世界,而问题在于改变世界”?
    查看>>
    逗号只能存在于数字中间
    查看>>
    .NET平台微服务项目汇集
    查看>>
    Dubbo-admin管理平台的安装
    查看>>
    DDD中的值对象如何用NHibernate进行映射
    查看>>
    【设计】概要设计-详细设计-到底需要输出什么???
    查看>>
    部署 k8s Cluster(下)- 每天5分钟玩转 Docker 容器技术(119)
    查看>>