Table of contents
1 Google 以圖搜尋
相信大家都知道 Google 搜尋既其中一個叫做 Google Images 既功能可以幫我地上載圖片去搜尋更多圖片,呢個功能已經存在多年。
佢最有用既一個用途係,我地可以上載一張低清圖片,然後 Google Images 會顯示所有相同或相似既圖片結果,然後我地可以喺結果頁面選擇最高清既解像度,Google Images 會以相反次序將結果顯示,一目了然,非常方便。
不過,Google 近年就將 Google Images 既以圖搜尋功能轉左用 Google Lens,亦都冇左重要既功能——解像度既選項、排序。
2 需求
根據而家 Google Lens 搜尋結果頁面既設計,我地係冇任何選項,只能夠撳「More exact matches」既掣去顯示更多搜尋結果。
所以我地需要:
- 透過自動撳「More exact matches」既掣去顯示所有搜尋結果。
- 以解像度既相反次序去排序搜尋結果。
- 避免因為頁面自動刷新而失去我地對頁面既改動。
3 動手寫
3.1 顯示所有搜尋結果
3.1.1 Locate 「More exact matches」掣
Locate 「More exact matches」既掣:
const els = [...document.querySelectorAll("div")]
.filter((e) => e.children.length === 0)
.filter((e) => e.innerText === "More exact matches");
const el = els[0];
解釋:
- 因為 Google Lens 既頁面既 DOM elements 都冇
id
,而佢地既 class names 似係 front-end library 生成既隨機值,所以我地只能用個掣既顯示文字去搵返佢個 div
element 出黎。
3.1.2 自動撳掣機制
1const clickMore = () => {
2 const els = [...document.querySelectorAll("div")]
3 .filter((e) => e.children.length === 0)
4 .filter((e) => e.innerText === "More exact matches");
5
6 if (els.length > 0) {
7 els[0].click();
8 setTimeout(clickMore, 500);
9 }
10};
11
12clickMore();
解釋:
- 考慮到撳掣之後可能會需要一啲時間,所以我地會用
setTimeout
去每隔一小段時間去一直 check 下個掣存唔存在,然後撳一次。
- 如果個掣唔再存在,我地就唔需要再繼續 check。
3.2 排序搜尋結果
1const extractWidth = (node) => Number.parseInt(node.innerText.replace(/.*?(\d+)x(\d+)/, '$1'));
2const extractHeight = (node) => Number.parseInt(node.innerText.replace(/.*?(\d+)x(\d+)/, '$2'));
3
4const sortAllResults = () => {
5 const newListEl = document.createElement("ul");
6
7 const listEl = document.querySelector('ul[aria-label="All results list"]');
8
9 [...listEl.children]
10 .sort((a, b) => {
11 const aEl = [...a.querySelectorAll('div')].filter(e => e.children.length === 0 || [...e.children].every(o => o.tagName.toLowerCase() === 'span')).filter(e => /.*?(\d+)x(\d+)/.test(e.innerText))[0];
12 const bEl = [...b.querySelectorAll('div')].filter(e => e.children.length === 0 || [...e.children].every(o => o.tagName.toLowerCase() === 'span')).filter(e => /.*?(\d+)x(\d+)/.test(e.innerText))[0];
13
14 const aArea = aEl ? (extractWidth(aEl) * extractHeight(aEl)) : 0;
15 const bArea = bEl ? (extractWidth(bEl) * extractHeight(bEl)) : 0;
16
17 return bArea - aArea;
18 })
19 .forEach(e => {
20 newListEl.appendChild(e);
21 });
22
23 document.body.replaceChildren(newListEl);
24};
解釋:
- 我地會用每張圖片既解像度數字去計出圖片既面積,然後以面積既相反次序去排序搜尋結果。
- 我地會將搜尋結果既
li
DOM elements 加到一個新既 ul
list,再將 body 既內容換成呢個新既 ul
list,從而避免因為 parent DOM elements 自動刷新而失去我地對 ul
既 li
DOM elements 排序既改動。
4 包裝成 Chrome extension
我地可以寫一個 Chrome extension 去自動執行以上既 script。
4.1 檔案
只需要 2
個檔案:
檔案 | 用途 |
---|
manifest.json | 關於呢個 Chrome extension 既基本資訊,例如呢個 Chrome extension 適用於邊啲 URL patterns。 |
content.js | 真正要執行既 script。 |
我地要將佢地放埋喺同一個 folder 裡面。
4.1.1 manifest.json
格式如下:
1{
2 "name": "Google-Lens-Sort",
3 "version": "1.0",
4 "description": "Sort Google Lens image search results",
5 "content_scripts": [
6 {
7 "matches": [
8 "*://lens.google.com/search*"
9 ],
10 "js": ["content.js"],
11 "run_at": "document_idle"
12 }
13 ],
14 "manifest_version": 3
15}
註:
- 經過測試後,發現只有
run_at
係 document_idle
既情況下,我地既 content.js
script 先會喺頁面由 Google Images redirect 去 Google Lens 結果頁之後執行。
- 如果
run_at
係 document_start
,咁就要先手動 refresh Google Lens 搜尋結果頁,咁個 script 先會執行。
4.1.2 content.js
經過優化後,以下係最終既 script。
以下既格式其實可以用作 Chrome extensions 既 template。
1const MAX_RUN = Infinity;
2const DELAY_INCREMENT = 1000;
3const DELAY_MAX = 1000;
4var delay = 0;
5var done = false;
6
7const clickMore = () => {
8 const els = [...document.querySelectorAll('div')]
9 .filter(e => e.children.length === 0)
10 .filter(e => e.innerText === 'More exact matches');
11
12 if (els.length > 0) {
13 els[0].click();
14 console.log("Clicked button 'More exact matches'");
15 setTimeout(clickMore, 500);
16 } else if (sortAllResults()) {
17 done = true;
18 }
19};
20
21const extractWidth = (node) => Number.parseInt(node.innerText.replace(/.*?(\d+)x(\d+)/, '$1'));
22const extractHeight = (node) => Number.parseInt(node.innerText.replace(/.*?(\d+)x(\d+)/, '$2'));
23
24/* For troubleshooting, run below in browser console
25
26const extractWidth = node => Number.parseInt(node.innerText.replace(/.*?(\d+)x(\d+)/, '$1'));
27[...document.querySelectorAll('ul[aria-label="All results list"] li div')]
28 .filter(e => e.children.length === 0 || [...e.children].every(o => o.tagName.toLowerCase() === 'span'))
29 .filter(e => /.*?(\d+)x(\d+)/.test(e.innerText))
30 .toSorted((a, b) => extractWidth(b)-extractWidth(a))
31 .map(extractWidth)
32*/
33
34const sortAllResults = () => {
35 const newListEl = document.createElement("ul");
36 newListEl.setAttribute('aria-label', 'All results list');
37 newListEl.style.width = '70%';
38
39 const listEl = document.querySelector('ul[aria-label="All results list"]');
40
41 if (!listEl) {
42 return;
43 }
44
45 [...listEl.children]
46 .sort((a, b) => {
47 const aEl = [...a.querySelectorAll('div')].filter(e => e.children.length === 0 || [...e.children].every(o => o.tagName.toLowerCase() === 'span')).filter(e => /.*?(\d+)x(\d+)/.test(e.innerText))[0];
48 const bEl = [...b.querySelectorAll('div')].filter(e => e.children.length === 0 || [...e.children].every(o => o.tagName.toLowerCase() === 'span')).filter(e => /.*?(\d+)x(\d+)/.test(e.innerText))[0];
49
50 const aArea = aEl ? (extractWidth(aEl) * extractHeight(aEl)) : 0;
51 const bArea = bEl ? (extractWidth(bEl) * extractHeight(bEl)) : 0;
52
53 return bArea - aArea;
54 })
55 .forEach(e => {
56 newListEl.appendChild(e);
57 });
58
59 document.body.replaceChildren(newListEl);
60
61 return true;
62};
63
64const run = () => {
65 let i = 0;
66
67 const _run = () => {
68 if (i === MAX_RUN) {
69 return;
70 }
71
72 i++;
73 console.log('Running iteration ' + i);
74
75 if (done) {
76 return;
77 } else {
78 clickMore();
79 }
80
81 setTimeout(_run, delay);
82 delay = Math.min(delay + DELAY_INCREMENT, DELAY_MAX);
83 };
84
85 _run();
86};
87
88run();
4.2 安裝 Chrome extension
- 喺 Chrome 打開
chrome://extensions
頁面。
- 開啟 Developer mode。
- Load unpacked ➜ 打開裝住
manifest.json
既 folder。
5 測試
5.1 手動執行
我地唔一定要用 Chrome extension 去自動執行我地段 script。
喺冇或者禁用左 Chrome extension 既情況下:
- 打開 Google Images 頁面。
- 將測試圖片 drag 到圖片上載框內。
- 撳圖片上既「Find image source」掣。
- 打開 Chrome developer tools 既 Console tab。
- 將段 script paste 落去,然後按 Enter 執行。
- 等頁面上既搜尋結果改變。
5.2 以 Chrome extension 自動執行
喺 Chrome extension 生效既情況下:
- 打開 Google Images 頁面。
- 將測試圖片 drag 到圖片上載框內。
- 撳圖片上既「Find image source」掣。
- 等頁面上既搜尋結果改變。