owned this note
owned this note
Published
Linked with GitHub
# [Python]Plurk 回應截圖長圖
## 目標
~~1. 網頁截圖~~
~~2. 使用者登入,支援私噗~~
~~3. 按照回應截圖並且拼圖~~
~~4. 使用這可以設定長圖的最長長度,自動分割圖片~~
5. 使用元素的大小與座標進行圖片裁切
6. 使用Tk 作為簡易GUI 使用介面,減輕使用者的壓力
7. 使用pyinsaller 打包windows exe 上傳到dropbox
## 規劃
1. 單純直接最大瀏覽器高度 會出現bug
2. 打算固定瀏覽器的高度 並且用高度去進行計算要截圖幾次
## 目前使用到的Library
1. selenium
2. numpy
3. Pillow (PIL)
4. pyinstaller
## License
[python](https://docs.python.org/3/license.html) PSF (BSD-Like)
[selenium](https://www.seleniumhq.org/about/license.jsp) Apache 2.0 License.
[numpy](http://www.numpy.org/license.html)
[Pillow](https://github.com/python-pillow/Pillow/blob/master/LICENSE#L12)
[pyinstaller](https://pythonhosted.org/PyInstaller/license.html) **GPL License**
~~竟然有GPL 授權,怕。~~
那我就用[MIT 授權](https://zh.wikipedia.org/wiki/MIT授權)就好了
Copyright 2018 KUO/YUTI
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## 第一版本
這種方法有極限最長的長度限制,可能要做成限制瀏覽器最長為單位去換算要拉幾次
```
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import getpass
driver = webdriver.Chrome()
driver.get("https://www.plurk.com/p/mpv3ly")
wait = WebDriverWait(driver, 10)
login_elem = wait.until(EC.element_to_be_clickable((By.ID, 'bar-login')))
login_elem.click()
nick_name_elem = wait.until(EC.element_to_be_clickable((By.NAME, 'nick_name')))
password_elem = wait.until(EC.element_to_be_clickable((By.NAME, 'password')))
register_submit_elem = wait.until(EC.element_to_be_clickable((By.ID, 'register_submit')))
nick_name_elem.click()
nick_name_elem.send_keys(str(input('UserName:')))
password_elem.click()
password_elem.send_keys(str(getpass.getpass('Password:')))
register_submit_elem.click()
layout_content_html_elem = wait.until(EC.element_to_be_clickable((By.ID, 'layout_content_html')))
driver.set_window_size(layout_content_html_elem.size['width'],layout_content_html_elem.size['height'])
top_bar_elem = wait.until(EC.element_to_be_clickable((By.ID, 'top_bar')))
driver.execute_script("window.scrollTo(0, {0});".format(top_bar_elem.size['height']))
driver.save_screenshot('test.png')
```
## 第二版本
已經可以擷取並且拼圖超過500 回應的噗
目前的拼圖只是一般的拼圖,第三版本會採用以div為單位的截圖與拼圖,可能會拼出不同大小的圖片
```
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import getpass
import math
HEIGHT = 1000.0
SCROLL_HEIGHT = HEIGHT
#driver = webdriver.Chrome()
driver_options = webdriver.ChromeOptions()
#driver_options.add_argument("--kiosk")
driver_options.set_headless() # indeed
driver = webdriver.Chrome(options=driver_options)
print("loading...")
driver.get("https://www.plurk.com/p/mpv3ly")
wait = WebDriverWait(driver, 10)
login_elem = wait.until(EC.element_to_be_clickable((By.ID, 'bar-login')))
login_elem.click()
print("waitting...")
nick_name_elem = wait.until(EC.element_to_be_clickable((By.NAME, 'nick_name')))
password_elem = wait.until(EC.element_to_be_clickable((By.NAME, 'password')))
register_submit_elem = wait.until(EC.element_to_be_clickable((By.ID, 'register_submit')))
nick_name_elem.click()
nick_name_elem.send_keys(str(input('UserName:')))
password_elem.click()
password_elem.send_keys(str(getpass.getpass('Password:')))
register_submit_elem.click()
print("login...")
print("waitting...")
layout_content_html_elem = wait.until(EC.element_to_be_clickable((By.ID, 'layout_content_html')))
driver.set_window_size(layout_content_html_elem.size['width'],HEIGHT)
top_bar_elem = wait.until(EC.element_to_be_clickable((By.ID, 'top_bar')))
driver.execute_script("window.scroll(0, {0});".format(top_bar_elem.size['height']))
for x in range(math.ceil(float(layout_content_html_elem.size['height'])/SCROLL_HEIGHT)):
driver.execute_script("window.scroll(0, {0});".format(SCROLL_HEIGHT*x))
driver.save_screenshot("test_{0}.png".format(x))
print("saving...test_{0}.png".format(x))
import os
import re
filelist=os.listdir('.')
for fichier in filelist[:]: # filelist[:] makes a copy of filelist.
if not(fichier.endswith(".png")):
filelist.remove(fichier)
filelist.sort(key=lambda x:int(re.search('(\d+)',x).group(0)))
print(filelist)
import numpy as np
from PIL import Image
pic_numbers = 2
combine_numbers = np.arange(0,len(filelist),pic_numbers)[:]
last_one_number = int(re.search('(\d+)',filelist[-1]).group(0))
if last_one_number % pic_numbers > 0:
np.append(combine_numbers,combine_numbers[-1]-(pic_numbers- (last_one_number % pic_numbers)))
for x in combine_numbers:
list_im = filelist[x:x+(pic_numbers)]
print(list_im)
imgs = [ Image.open(i) for i in list_im ]
# pick the image which is the smallest, and resize the others to match it (can be arbitrary image shape here)
min_shape = sorted( [(np.sum(i.size), i.size ) for i in imgs])[0][1]
# for a vertical stacking it is simple: use vstack
imgs_comb = np.vstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) )
imgs_comb = Image.fromarray( imgs_comb)
imgs_comb.save( 'combine_vertical_{0}.png'.format(int(x/pic_numbers)) )
print('saving...combine_vertical_{0}.png'.format(int(x/pic_numbers)))
```
## 第三版本
根據li 元件的單位大小進行擷取
數量大小可以設定
接下來要製作使用者界面
```
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import getpass
import math
import numpy as np
from PIL import Image
import io
import math
HEIGHT = 3000.0
SCROLL_HEIGHT = HEIGHT
driver_options = webdriver.ChromeOptions()
driver_options.set_headless() # indeed
driver = webdriver.Chrome(options=driver_options)
print("loading...")
driver.get("https://www.plurk.com/")
wait = WebDriverWait(driver, 10)
login_elem = wait.until(EC.element_to_be_clickable((By.ID, 'bar-login')))
login_elem.click()
print("waitting...")
nick_name_elem = wait.until(EC.element_to_be_clickable((By.NAME, 'nick_name')))
password_elem = wait.until(EC.element_to_be_clickable((By.NAME, 'password')))
register_submit_elem = wait.until(EC.element_to_be_clickable((By.ID, 'register_submit')))
nick_name_elem.click()
nick_name_elem.send_keys(str(input('UserName:')))
password_elem.click()
password_elem.send_keys(str(getpass.getpass('Password:')))
register_submit_elem.click()
print("login...")
driver.get(str(input('Plurk_Web_Address:')))
print("waitting...")
permanent_content_elem = wait.until(EC.element_to_be_clickable((By.ID, 'permanent_content')))
#driver.set_window_size(permanent_content_elem.size['width'],HEIGHT)
width = permanent_content_elem.size['width']
permanent_plurk_article_elem = wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="permanent_plurk"]/article')))
driver.set_window_size(width,permanent_plurk_article_elem.size['height'])
driver.execute_script("window.scrollTo(0, {0});".format(permanent_plurk_article_elem.location['y']))
image_buffer = Image.open(io.BytesIO(driver.get_screenshot_as_png()))
image_buffer = image_buffer.crop((5, 0, image_buffer.size[0]-30, image_buffer.size[1]))
image_buffer.save("top.png")
reply_elem = driver.find_elements_by_xpath('//*[@id="reply"]/ul/li')
BUFFER_SIZE = 10
MAX_COMBINE = math.floor(len(reply_elem)/BUFFER_SIZE)*BUFFER_SIZE
x = 0
image_buffer = list()
image_obj_buffer = list()
for x in range(BUFFER_SIZE):
image_buffer.append(0)
x = 0
z = 0
for elem in reply_elem[0:MAX_COMBINE]:
driver.set_window_size(width,elem.size['height'])
driver.execute_script("window.scrollTo(0, {0});".format(elem.location['y']))
image_buffer[x%BUFFER_SIZE] = driver.get_screenshot_as_png()
print("saving...reply_{0}.png".format(x))
if(x % BUFFER_SIZE is (BUFFER_SIZE-1)):
y = 0
for y in range(BUFFER_SIZE):
image_buffer[y] = Image.open(io.BytesIO(image_buffer[y])) # convert to Image Object
image_buffer[y] = image_buffer[y].crop((5, 0, image_buffer[y].size[0]-30, image_buffer[y].size[1]))
#min_shape = sorted( [(np.sum(i.size), i.size ) for i in image_buffer])[0][1]
#imgs_comb = np.vstack( (np.asarray( i.resize(min_shape) ) for i in image_buffer ) )
imgs_comb = np.vstack( (np.asarray( i ) for i in image_buffer ) )
imgs_comb = Image.fromarray( imgs_comb)
imgs_comb.save( 'combine_vertical_{0}.png'.format(int(z)) )
print('saving...combine_vertical_{0}.png'.format(int(z)) )
z = z + 1
x = x + 1
x = 0
image_buffer = list()
image_obj_buffer = list()
for x in range(len(reply_elem) - MAX_COMBINE):
image_buffer.append(0)
x = 0
for elem in reply_elem[MAX_COMBINE:]:
driver.set_window_size(width,elem.size['height'])
driver.execute_script("window.scrollTo(0, {0});".format(elem.location['y']))
image_buffer[x%BUFFER_SIZE] = driver.get_screenshot_as_png()
#driver.save_screenshot("reply_{0}.png".format(x))
print("saving...reply_{0}.png".format(x))
if(x is len(image_buffer)-1):
y = 0
for y in range(len(image_buffer)):
image_buffer[y] = Image.open(io.BytesIO(image_buffer[y])) # convert to Image Object
#image_buffer[y].save("reply_{0}.png".format(y))
image_buffer[y] = image_buffer[y].crop((5, 0, image_buffer[y].size[0]-30, image_buffer[y].size[1]))
#min_shape = sorted( [(np.sum(i.size), i.size ) for i in image_buffer])[0][1]
#imgs_comb = np.vstack( (np.asarray( i.resize(min_shape) ) for i in image_buffer ) )
imgs_comb = np.vstack( (np.asarray( i ) for i in image_buffer ) )
imgs_comb = Image.fromarray( imgs_comb)
imgs_comb.save( 'combine_vertical_last.png')
print('saving...combine_vertical_last.png')
x = x + 1
print('Done !')
driver.close()
```
## 第四版本
jquery highlight
include jquery in chrome console
[在Chrome的Console使用jQuery / How to include jQuery in Chrome's Console](http://blog.pulipuli.info/2017/01/chromeconsolejquery-how-to-include.html)
```
javascript:(function(e,s){e.src=s;e.onload=function(){jQuery.noConflict();$=jQuery;console.log('jQuery injected')};document.head.appendChild(e);})(document.createElement('script'),'https://code.jquery.com/jquery-latest.min.js')
```
修改暱稱的顏色,可以做到highlight 的效果
```
$("a:contains('暱稱')").css( "color", "顏色" );
$("*:contains('暱稱')").css( "color", "顏色" );
```