原创:用python把链接指向的网页直接生成图片的http服务及网站(含源码及思想)

原创:用python把链接指向的网页直接生成图片的http服务及网站(含源码及思想)


总体思想:
    希望让调用方通过 http调用传入一个需要生成图片的网页链接生成一个网页的图片并返回图片链接
    最终调用方式比如:http://127.0.0.1:8888/cgi-bin/test.py?url=http://www.csdn.net/
    上述范例打开之后返回的是 http://www.csdn.net/ 这个网页最终生成的图片的链接
    这么做的目的就是让调用的人几乎不用动脑,当然实现这个的人也会觉得牛逼哄哄:)


    要实现上述这个思想,分解一下就需要实现以下功能:
    1、需要做一个网站
    2、这个网站还需要能执行 python的脚本也就是说要具备cgi处理功能
    3、需要一个代码能对给定的网址保存为图片
    4、我们需要一款简单又可靠的又能执行python脚本的web server


    通过google.com经过一番对python的简单了解之后我们发现上述四个目标其实都非常简单:
    1、python自带了一个cgi web server启动方式非常之简单,基本上只需要小学文化:
       在linux终端或这windows的dos窗口运行: python -m CGIHTTPServer 8888
       就实现了目标1和目标2
    2、测试一下我们的这个web server究竟管用不管用
       在当前目录下建立一个index.html 输入it works!
       好了在浏览器中输入:http://127.0.0.1:8888/index.html 
       看到了吧,就这么简单,仅需小学文化水平:)
    3、建立一个简单的python脚本程序,让浏览器能够打开,同时也能接收一个url参数
       比如:http://127.0.0.1:8888/cgi-bin/pycgidemo.py?url=http://www.csdn.net/
       在当前目录下建立一个目录cgi-bin 然后在里面建立一个文本文件:pycgidemo.py 输入:
       #!/usr/bin/env python
       =======================================pycgidemo.py====begin========================
# -*- coding: utf-8 -*-


import os
import io
import time
import JSInjection
from PIL import Image
from selenium import webdriver
import html2image
import uuid


if __name__ == '__main__':
   v = [i for i in os.environ.iteritems()]
   print('env is %s' %(repr(v)))
   qs={k:v for k,v in [l.split('=') for l in os.environ['QUERY_STRING'].split('&')]}
   print('Content-type: text/html\n')
   print('<title>pycgidemo</title>')
   print('Hello World<br/>')
   print(qs['url'])


   quit()
        =======================================pycgidemo.py====end========================
好了,测试一下,在浏览器中输入:http://127.0.0.1:8888/cgi-bin/pycgidemo.py?url=http://www.csdn.net/
浏览器上显示:Hello World 
             http://www.csdn.net/
浏览器输入的地址就是我们的cgi脚本程序,同时还接收了一个参数url=这个后面的就是参数,
可以随便输入,输入什么浏览器就显示什么
看到了吧,就这么简单,仅需初中文化水平:)
    4、web server有了,cgi脚本也能处理了,网站也有了,下一步就是写一个程序能将给定的url生成图片
       这个问题看起来很天方夜谭,其实答案是有的,那就是url能生成图片肯定需要有一个浏览器,
       那么问题来了,a、浏览器能被调用吗 b、浏览器能把打开的网页保存成图片吗
       这两个问题其实不难回答:UC、360浏览器、QQ浏览器、搜狗浏览器这些是什么玩意呢?
          说穿了这些都是垃圾,为什么这么说呢,因为他们连开发浏览器的最基本能力都没有,仅仅是把:
 IE浏览器或者google浏览器拿过来包装了一下装个逼而已,既然如此咱们岂不是照样可以装逼:)
 
  答案是yes,既然要装个逼,看看下面我怎么装逼的啊:(别害怕,有大师在,你其实具备初中文化水平足以)
       
第一步 配置运行及开发环境:
   1、操作系统:linux或 windows 实际采用的是 centos linux的这一种
   2、python2.x、或python3.x 实际采用的是centos自带的python2.7


第二步 编译:phantomjs (这是一个开源的能自动化控制的浏览器)
sudo yum install gcc gcc-c++ make git openssl-devel freetype-devel fontconfig-devel 
git clone git://github.com/ariya/phantomjs.git 
cd phantomjs 
git checkout 1.9 
./build.sh 
第三步 安装python相关库
pip install selenium (这个库强大,能操纵各种浏览器包括:phantomjs、google、firefox等)
pip install pillow
第四步 配置 phantomjs
vi /etc/profile
添加:
export PHANTOMJS_HOME=/soft/phantomjs
PATH=$PATH:$PHANTOMJS_HOME/bin


:wq #保存
source /etc/profile #生效


第五步 写一个脚本来把url打开并保存为图像:
这里我写了两个简单的python脚本
1、html2image.py  这个脚本的作用就是打开url然后保存为图片
2、JSInjection.py 这个脚本的作用是考虑到某些网页比较长,
       而且内容不滚动到那里就不显示,那么我就用这个让它自动滚动到底
3、测试及调用方式很简单:
   urltoimage = html2image("http://www.csdn.net/", JSInjection.Scroll2Bottom(), '/soft/cgi-bin/aaa.jpg')
   urltoimage.save_image()
   执行这两行代码之后就会将  http://www.csdn.net的首页自动保存为aaa.jpg并保存到 /soft 目录下
   简单不,这里仅需小学文化水平:)
4、下面来看看咱们这两个脚本的代码哈:
=======================================html2image.py====begin========================
# -*- coding: utf-8 -*-


import io
import time
import JSInjection
from PIL import Image
from selenium import webdriver


__author__ = '黄智(小白救星)'
__version__ = 1.0




class html2image:
    def __init__(self, url, jscode, mfilename):
        #service_log_path = '/soft/chromedriver/chromedriver.log'
        #service_args = ['--verbose', '--no-sandbox']
        
        #driver = webdriver.Chrome('/soft/chromedriver/bin/chromedriver', service_args=service_args, service_log_path=service_log_path) 
        #driver.get('http://www.baidu.com/')
        #driver.quit()
        
        #print('finished')


        #chrome_options = webdriver.ChromeOptions()
        #chrome_options.add_argument('--no-sandbox')
        #self.browser = webdriver.Chrome('/soft/chromedriver', chrome_options=chrome_options)


        #opts = webdriver.ChromeOptions()
        #opts.executable_path="/soft/chromedriver/bin"
        #opts.binary_location="/soft/chromedriver/bin/chromedriver"
        #opts.add_experimental_option("excludeSwitches", ["ignore-certificate-errors"])
        #opts.service_log_path = '/soft/chromedriver/chromedriver.log'
        #opts.add_argument('--no-sandbox')
        #opts.service_args = ['--verbose', '--no-sandbox']
        #self.browser = webdriver.Chrome('/soft/html2image/html2image/cgi-bin/chromedriver', service_args = service_args,service_log_path = service_log_path)
        #self.browser = webdriver.Chrome('/soft/chromedriver/bin/chromedriver', chrome_options=opts)
        #self.browser = webdriver.Chrome(chrome_options=opts)


        #self.browser = webdriver.Chrome(executable_path='/soft/html2image/html2image')
        #self.browser = webdriver.Chrome(executable_path='/soft/chromedriver64',
        #                                service_log_path=r"/soft/html2image/html2image/wlog.log")
        self.browser = webdriver.PhantomJS(service_log_path=r"/soft/html2image/html2image/wlog.log")
        self.browser.set_window_size(1200, 900)
        self.browser.get(url)
        self.jscode = jscode
        self.image = None
        self.filename = mfilename


    def get_image(self):
        if self.jscode is not None and isinstance(self.jscode, JSInjection.JSCode) and self.jscode.get_jscode != "":
            # print(self.jsCode.getJSCode())
            self.browser.execute_script(self.jscode.get_jscode())
            #for i in range(30):
            #    # print(self.browser.title)
            #    if self.jscode.finished_sign in self.browser.title:
            #        break
            #    time.sleep(10)


        self.image = self.browser.get_screenshot_as_png()
        # self.browser.close()
        return self.image


    def get_element_image(self, css_selector):
        if self.image is None:
            self.get_image()
        element = self.browser.find_element_by_css_selector(css_selector)
        left, top = element.location['x'], element.location['y']
        right = left + element.size['width']
        bottom = top + element.size['height']
        im = Image.open(io.BytesIO(self.image))
        im = im.crop((left, top, right, bottom))
        # im.show()
        img_byte_arr = io.BytesIO()
        im.save(img_byte_arr, format='JPEG')
        return img_byte_arr.getvalue()


    def save_image(self, image=None):
        if image is None:
            if self.image is None:
                image = self.get_image()
            else:
                image = self.image
        try:
            with open(self.filename, 'wb') as f:
                f.write(image)
        except IOError:
            return False
        finally:
            del image
        return True


    def __del__(self):
        self.browser.close()
=======================================html2image.py====end==========================


=======================================JSInjection.py====begin=======================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-


"""This is a JavaScript injection model."""
from abc import abstractmethod, ABCMeta


__author__ = '黄智(小白救星)'
__version__ = 1.0




class JSCode:
    __metaclass__ = ABCMeta


    finished_sign = "Hi, I completed the JS injection!"
    # 请在js代码表示执行完毕的位置加入`finished_code`,外部函数会根据网页title中是否包含`finished_sign`来判断js代码是否执行完毕
    # js函数运行是异步的方式, 若js代码里有函数执行,直接把`finished_code`加到最后是行不通的
    # 虽然有方法可以把函数改成同步方式,但没想到通式的方法(js盲),只能做这样处理了.
    finished_code = "document.title += '" + finished_sign + "';"


    @abstractmethod
    def get_jscode(self):
        return ""




class Scroll2Bottom(JSCode):
    def get_jscode(self):
        return """(function () {
            var y = 0;
            var step = 100;
            var height = document.body.scrollHeight;
            function f() {
                //if (y < document.body.scrollHeight) {//这种情况下, 若html下拉会触发加载更多,会陷入循环内,直到加载完所有数据或函数运行时间超时
                if (y < height) { //这种情况下, 还是会触发加载更多, 但不会陷入循环中, 得到的图片可以进行裁剪, 或者根据屏幕分辨率事先算好触发加载更多的边界条件
                    y += step;
                    //window.document.body.scrollTop = y;
                    window.scrollTo(0, y);
                    setTimeout(f, 100);
                } else {
                    window.scrollTo(0, 0);""" + self.finished_code + """
                }
            }
            setTimeout(f, 1000);
        })();"""
=======================================JSInjection.py====end=========================


第六步 改造一下我们之前写的 pycgidemo.py让他来调用 我们上面的图片生成及保存代码
这里暂时新建一个文件,放在 cgi-bin目录下,就叫 test.py吧或者叫做test.cgi也可以
=======================================test.py====begin=========================
#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import io
import time
import JSInjection
from PIL import Image
from selenium import webdriver
import html2image
import uuid


if __name__ == '__main__':
    v = [i for i in os.environ.iteritems()]
    print('env is %s' %(repr(v)))
    qs={k:v for k,v in [l.split('=') for l in os.environ['QUERY_STRING'].split('&')]}
    print('Content-type: text/html\n')
    print('<title>urlsavetoimage</title>')
    u=str(uuid.uuid1())
    filename = '/soft/'+u+'.jpg'
    h2i = html2image.html2image(qs['url'], JSInjection.Scroll2Bottom(), filename)
    h2i.save_image()
    print('<a href="'+'/'+u+'.jpg'+'" target=_blank>'+'/'+u+'.jpg'+'</a>')
    quit()
=======================================test.py====end==========================


第七步 打开浏览器输入:http://127.0.0.1:8888/cgi-bin/test.py?url=http://www.csdn.net/
url后面的链接可以随意修改,这个过程稍微有点慢,大概需要机秒钟时间,
当然了这是因为java、python、PHP、C# 这些脚本语言本身就是这么垃圾,没办法啊,
想快的话参考我这个小工具:
    http://blog.csdn.net/tengyunjiawu_com/article/details/76228675
    链接对应的网页保存为图片,用时:瞬间,适合任何网页,这个纯编译型语言开发的哦


第八步:处理中文乱码问题
yum install bitmap-fonts bitmap-fonts-cjk


大功告成!整个过程仅需中小学文化程度:)

写程序其实很简单,本人精通,sorry,是熟练:)掌握C、C++、Java、PHP、Python、Delphi、C# 等数十种编程语言,从来没觉得有啥牛逼之处,

写代码关键还是思想:思考问题、分析分解问题才是关键,工具没有高低之分,只有思想境界的差距:)

本人原创,未经许可可随意转载!
posted @ 2017-07-28 22:04  chinacloudy  阅读(2216)  评论(0编辑  收藏  举报