検出した画像をページ上で示す

「Edit Yahoo Img」を使うと、対象画像が検出されたページに直接に移動できます。 開いたブログページで問題の画像を探すと、それがすぐに判る場合もありますが、中にはどこに問題の画像があるか判らない事があります。

 

DevTools を使ってHTMLを調べると、検出された画像が漸く見つかるのですが、これでは不便です。 ページ上に多数の画像があり、その一部だけが対象画像の場合もあります。 スタッフブログの告知の通り、クリックでYahooのエラー画面が出る場合と、告知とは違ってYahooのリンクが設定されていない場合もあります。 その場合は、クリックでエラー画面は表示されません。 しかし DevToolsで調べると(あるいは再編集画面のHTML表示で調べると)やはり「Search Yahoo Img」が検出した通りの要対策画像だったりします。

 

問題をできるだけ早く解決するには、問題の画像を明確に示す必要があります。 また、画像が表示されない場合も、どこに調べるべきSRCコードがあるか、ユーザーに明瞭に教える必要があります。

 

「Edit Yahoo Img」に、この問題箇所を示す機能を実装しました。 対象のページを開くと、デフォルトで問題の画像に「赤点滅」のマークが表示されます。 パネル中央の「記事のデータ表示枠」 には、ページで検出された画像数が表示されますから、ページ本文を見て、対象画像の数のマークの場所を先ず確認すべきでしょう。

 

 

 

対象の画像を示すマーク 

以下は、調査で検出した画像の場所を示すマークの様子です。

 

画像が表示されないケースでは、ブラウザが用意した「画像欠損」を示すアイコンが代わりに表示される場合が多く、下の様な状態になります。「」のマークが、検出した場所を示して点滅しています。

 

 

画像が表示されるケースでは、一般に画像の隅にこのマークが表示されます。

 

 

ただし、画像が中央配置になっている場合は、マークが本文枠の左端に離れますが、対象の画像は判断できるでしょう。

 

 

このマークは、ページを開いた時から点滅して場所を示します。

 

 

 

 「Edit Yahoo Img」を使用するには 

「Edit Yahoo Img」は Chrome/Edge/Firefox の拡張機能「Tampermonkey」上で動作するスクリプトツールです。 上記のブラウザで動作を確認していますが、他のブラウザでの動作は未確認です。

 

❶「Tampermonkey」を導入します

◎ 使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。

既に「Tampermonkey」を導入している場合は、この手順 ❶ は不要です。 

拡張機能の導入については、以下のページに簡単な説明があるので参照ください。

 

 

❷「Tampermonkey」にスクリプトを登録します

◎「Tampermonkey」の「」マークの「新規スクリプト」タブを開きます。

 

 

 

◎「新規スクリプト」には、最初から「新規のテンプレート」が記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。

 

〔コピー方法〕 軽量シンプルなツール「PreBox Button   」を使うと

  コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」

  の操作で、掲載コードのコピーが可能になります。

 

◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。

 

 

〔 Edit Yahoo Img 〕 ver. 0.3

 

// ==UserScript==
// @name         Edit Yahoo Img
// @namespace    https://ameblo.jp/
// @version      0.3
// @description  Yahoo由来の画像検索と編集
// @author       Ameblo User
// @match       https://ameblo.jp/*
// @grant        none
// ==/UserScript==


let ua=0; // Chromeの場合のフラグ
let agent=window.navigator.userAgent.toLowerCase();
if(agent.indexOf('firefox') > -1){ ua=1; } // Firefoxの場合のフラグ


let target=document.querySelector('head'); // 監視 target
let monitor=new MutationObserver(do_task);
monitor.observe(target, { childList: true }); // 監視開始

do_task();

function do_task(){
    let entry_id=0;
    let entryWrapper=document.querySelector('.js-entryWrapper');
    if(entryWrapper){
        entry_id=entryWrapper.getAttribute('data-unique-entry-id'); } // 現在のページID

    let pageDB; // 主データ配列 [entry-ID, 対象数, 処理フラグ, 投稿日付, 起動コントロール]
    let idDB=[]; // 記事idの配列

    let read_json=localStorage.getItem('CKIMG_DB'); // ローカルストレージ 保存名
    pageDB=JSON.parse(read_json);
    for(let k=0; k<pageDB.length; k++){
        idDB.push(pageDB[k][0]); } // 記事id検索用の配列を作る


    envi_0();
    envi_1();
    now_entry();
    pointer();


    function now_entry(){
        let done=document.querySelector('#done');
        let page_edit=document.querySelector('#page_edit');
        let entry_date;

        let time=document.querySelector('time');
        if(!time){
            entry_date=' '; }
        else{
            entry_date=time.getAttribute('datetime'); }

        if(entry_id!=0){
            let index=idDB.indexOf(entry_id);
            if(index!=-1){
                if(pageDB[index][2]==0){
                    done.style.boxShadow='inset 0 0 30px red'; // 未処理
                    if(pageDB[index][1]>1){
                        page_edit.value=pageDB[index][1] +'*'+ entry_id +' date: '+ entry_date; }
                    else{
                        page_edit.value=' '+ entry_id +' date: '+ entry_date; }}
                else{
                    done.style.boxShadow='inset 0 0 30px #00e2ff'; // 処理済
                    if(pageDB[index][1]>1){
                        page_edit.value=pageDB[index][1] +'*'+ entry_id +' date: '+ entry_date; }
                    else{
                        page_edit.value=' '+ entry_id +' date: '+ entry_date; }}}
            else{
                done.style.boxShadow='none';
                page_edit.value=' '+ entry_id +' date: '+ entry_date; }} // 対象外
        else{
            page_edit.value='    '; }}



    let done=document.querySelector('#done');
    done.onclick=function(event){ // マニュアルでマークを変更
        event.preventDefault();
        let index=idDB.indexOf(entry_id);
        if(index!=-1){
            if(pageDB[index][2]==0){
                pageDB[index][2]=1; // 処理済に変更
                done.style.boxShadow='inset 0 0 30px #00e2ff'; }
            else{
                pageDB[index][2]=0; // 未処理に変更
                done.style.boxShadow='inset 0 0 30px red'; }
            panel_color(index); } // リストパネルの配色を変更

        let write_json=JSON.stringify(pageDB);
        localStorage.setItem('CKIMG_DB', write_json); } // ローカルストレージ 保存



    function get_date_n(){
        let time=document.querySelector('time');
        if(time){
            let n_time=time.getAttribute('datetime');
            if(n_time){
                let m_time=n_time.replace(/-/g, '');
                return parseInt(m_time, 10); }}
        else return 0; }



    let go_next=document.querySelector('#go_next');
    go_next.onclick=function(){
        let active_arr=[];
        for(let k=1; k<pageDB.length; k++){
            if(pageDB[k][2]==0){ // 未処理の [記事ID,日付] 配列を作る
                active_arr.push([pageDB[k][0], pageDB[k][3]]); }}

        if(entry_id!=0){ // 記事一覧を開いている場合などを除外
            let now_index=active_arr.findIndex(item=> item[0]==entry_id);
            if(now_index!=-1){ // 現在 リスト中の記事ID
                if(now_index==active_arr.length-1){
                    alert("これより最近の未処理ページはありません"); }
                else{
                    goto(active_arr[now_index+1][0]); }}
            else{ // 現在 リスト外の記事ID  日付から次の記事を探す
                let entry_date=get_date_n();
                let next_index=active_arr.findIndex( item=> item[1] >= entry_date );
                if(next_index==-1){
                    alert("これより最近の未処理ページはありません"); }
                else{
                    goto(active_arr[next_index][0]); }}}}


    let go_back=document.querySelector('#go_back');
    go_back.onclick=function(){
        let active_arr=[];
        for(let k=pageDB.length-1; k>=1; k--){
            if(pageDB[k][2]==0){ // 未処理の [ページID,日付] 配列を作る(降順)
                active_arr.push([pageDB[k][0], pageDB[k][3]]); }}

        if(entry_id!=0){ // 記事一覧を開いている場合などを除外
            let now_index=active_arr.findIndex(item=> item[0]==entry_id);
            if(now_index!=-1){ // 現在 リスト中の記事ID
                if(now_index==active_arr.length-1){
                    alert("これより以前の未処理ページはありません"); }
                else{
                    goto(active_arr[now_index+1][0]); }}
            else{ // 現在 リスト外の記事ID  日付から次の記事を探す
                let entry_date=get_date_n();
                let back_index=active_arr.findIndex( item=> item[1] <= entry_date );
                if(back_index==-1){
                    alert("これより以前の未処理ページはありません"); }
                else{
                    goto(active_arr[back_index][0]); }}}}



    let page_edit=document.querySelector('#page_edit');
    page_edit.onclick=function(event){
        let article=document.querySelector('.js-entryWrapper');
        let path=
            'https://blog.ameba.jp/ucs/entry/srventryupdateinput.do?id='+entry_id;
        if(event.shiftKey){
            window.open(path, 0,'top=100, left=200, width=1020, height=900'); }
        else if(event.ctrlKey){
            window.location.href=path; }
        else{
            window.open(path, "_blank"); }}


    page_edit.onchange=panel_act(); // 記事一覧による移動に対応する



    let trial=document.querySelector('#trial'); // SRCの書換えで画像表示を試す
    trial.onclick=function(){
        let entryBody=document.querySelector('#entryBody');
        let imgs=entryBody.querySelectorAll('img');

        for(let k=0; k<imgs.length; k++){
            if(search_img(imgs[k])==1){
                trial_do(imgs[k]); }}

        function trial_do(target_img){
            let img_src=target_img.getAttribute('src');
            let new_src=img_src.replace('://other-', '://');
            target_img.setAttribute('src', new_src); }}



    function pointer(){
        let entryBody=document.querySelector('#entryBody');
        let imgs=entryBody.querySelectorAll('img');
        for(let k=0; k<imgs.length; k++){
            if(search_img(imgs[k])==1){
                point(imgs[k]); }}

        let links=entryBody.querySelectorAll('a');
        for(let k=0; k<links.length; k++){
            if(search_a(links[k])==1){
                point(links[k]); }}}


    function search_img(target_img){
        let pattern=[
            'yahoo.co.jp/IMG/ybi',
            'c.yimg.jp/res/blog',
            'west.edge.storage-yahoo.jp'].join('|');
        let url_regex=RegExp(pattern);

        let img_src=target_img.getAttribute('src');
        if(url_regex.test(img_src)==true){
            return 1; }}


    function search_a(target_link){
        let pattern=[
            'yahoo.co.jp/IMG/ybi',
            'c.yimg.jp/res/blog',
            'west.edge.storage-yahoo.jp'].join('|');
        let url_regex=RegExp(pattern);

        let link_href=target_link.getAttribute('href');
        if(url_regex.test(link_href)==true){
            return 1; }}


    function point(target_img){
        let insert_div;
        insert_div=document.createElement('div');
        insert_div.setAttribute('class', 'mark');
        insert_div.innerHTML="▲";
        let parent=target_img.parentNode;
        parent.style.position='relative';
        if(!parent.querySelector('.mark')){
            parent.insertBefore(insert_div, target_img); }}



    function envi_0(){
        let css=
            '#disp_yimg0 { position: absolute; top: 0; left: 0; font: 16px Meiryo; z-index: 1; '+
            'padding: 8px 0 6px; color: #000; background: aliceblue; width: 100%; } '+
            '#disp_yimg0 span { font: 16px Meiryo; color: #000; } '+
            '#num1, #num2 { width: 40px; margin-left: 4px; font: 16px Meiryo; '+
            'height: 18px; padding: 3px 8px 0; } '+
            '#go_next, #go_back { height: 26px; font-size: 16px; width: 60px; '+
            'box-shadow: inset 0 0 40px #87caff; } '+
            '#go_next { margin-left: 40px; } '+
            '#done { font: 16px Meiryo; width: 15px; margin-left: 15px; height: 26px; } '+
            '#page_edit { font: 16px Meiryo; height: 26px; width: 310px; text-align: left; '+
            ' padding-left: 6px; margin: 0 15px 0 2px; box-shadow: inset 0 0 30px #fff; } '+
            '#list_panel { margin-left: 30px; box-shadow: inset 0 0 40px #87caff; } '+
            '#trial { margin-left: 50px; box-shadow: inset 0 0 50px #5fd1c1; } '+
            '#page_sort { margin-left: 8px; box-shadow: inset 0 0 40px #5fd1c1; } '+
            '#storage { margin-left: 8px; padding: 0; line-height: 25px; vertical-align: 1px;'+
            'box-shadow: inset 0 0 20px #5fd1c1; } '+
            '.exe { font: 16px Meiryo; height: 26px; font-size: 16px; } '+

            '#i_panel { position: fixed; top: 50px; right: 10px; width: 300px; outline: 1px solid; '+
            'font: 16px Meiryo; color: #000; background: #fff; z-index: 4000; } '+
            '#items_ul { max-height: 60vh; overflow-y: scroll; list-style: none; '+
            'padding: 0px 0 0 22px; } '+
            '.item_li { margin: 6px 0; padding: 4px 0 1px 6px; line-height: 14px; cursor: pointer; } '+
            '.raw { box-shadow: -12px 0 0 red; } .dev { box-shadow: -12px 0 0 #00e2ff; } '+
            '.item_li:hover { background: #eee; } '+

            '.mark { position: absolute; color: red; font: 20px Meiryo; top: 6px; left: 0; '+
            ' animation: blinkAn .3s infinite alternate; -webkit-text-stroke: 1px #FFF; } '+
            '@keyframes blinkAn{ 0% { color: #fff; } 100%{ color: red; }} ';

        if(ua==1){
            css+='input[type="submit"] { background: unset; }'; }


        let style=document.createElement('style');
        style.setAttribute('id', 'style_yimg0');
        style.insertAdjacentHTML('afterbegin', css);
        if(!document.querySelector('#style_yimg0')){
            document.body.appendChild(style); } // CSSのセット


        let insert_div0;
        insert_div0=document.createElement('div');
        insert_div0.setAttribute('id', 'disp_yimg0');
        let ambHeader=document.querySelector('#ambHeader');
        if(!ambHeader.querySelector('#disp_yimg0')){
            ambHeader.appendChild(insert_div0); }

        insert_div0.innerHTML=
            '<span> 処理済</span>'+
            '<input id="num1" type="text" readonly>'+
            '<span> リスト</span>'+
            '<input id="num2" type="text" readonly>'+
            '<input id="go_next" type="submit" value="◀">'+
            '<input id="done" type="submit" value=" ">'+
            '<input id="page_edit" type="submit" value=" ">'+
            '<input id="go_back" type="submit" value="▶">'+
            '<input id="list_panel" class="exe" type="submit" value="▽&#x2006;List">'+
            '<input id="trial" class="exe" type="submit" value="❖&#x2006;Trial">'+
            '<input id="page_sort" class="exe" type="submit" value="♻&#x2006;Sort">'+
            '<input id="storage" class="exe" type="submit" value="🔅">';

    } // envi_0()



    function envi_1(){ // 処理済・リスト数を表示
        let num1=document.querySelector('#num1');
        let num2=document.querySelector('#num2');

        let undone=0;
        for(let k=0; k<pageDB.length; k++){
            if(pageDB[k][2]==1){
                undone+=1; }}

        num1.value=undone;
        num2.value=pageDB.length -1; }



    let page_sort=document.querySelector('#page_sort');
    page_sort.onclick=function(){ // 主配列を末尾の entry-date でソートする

        let conf_str=' 🟢 Yahoo画像の調査ツール「Search Yahoo Img」で\n'+
            '  調査や追加の調査を行った後は、データのソートを\n'+
            '  行ってください。\n'+
            '  ソートにより「Edit Yahoo Img」のページ送り順が\n'+
            '  ブログのページ順と同じになります。\n\n'+
            '    ソートする場合は「OK」を押してください';

        let ok=confirm(conf_str);
        if(ok){
            pageDB.sort((a, b)=>{ // 日付が若い順にソート
                if(a[3] > b[3]) return 1;
                if(a[3] < b[3]) return -1;
                return 0; });
            let write_json=JSON.stringify(pageDB);
            localStorage.setItem('CKIMG_DB', write_json); // ローカルストレージ 保存
            window.location.reload(); }}




    let list_panel=document.querySelector('#list_panel');
    list_panel.onclick=function(){
        let i_panel=document.querySelector('#i_panel');
        if(!i_panel || i_panel.style.display=='none'){
            disp_itemes(1); }
        else{
            disp_itemes(0); }}



    function disp_itemes(n){
        if(n==1){
            let i_panel=document.querySelector('#i_panel');
            if(!i_panel){ // リスト未作成
                let panel=document.createElement('div');
                panel.innerHTML=
                    '<ul id="items_ul"></ul>';
                panel.setAttribute('id', 'i_panel');
                document.querySelector('body').appendChild(panel);

                let items_ul=document.querySelector('#items_ul');
                if(items_ul){
                    items_ul.innerHTML='';
                    for(let k=1; k<pageDB.length; k++){
                        let item_li=document.createElement('li');
                        item_li.setAttribute('class', 'item_li');
                        if(pageDB[k][2]==0){
                            item_li.classList.add('raw'); }
                        else if(pageDB[k][2]==1){
                            item_li.classList.add('dev'); }
                        if(pageDB[k][1]>1){
                            item_li.innerHTML=pageDB[k][1] +'*'+ pageDB[k][0] + m(pageDB[k][3]); }
                        else{
                            item_li.innerHTML=' &#x2006;'+ pageDB[k][0] + m(pageDB[k][3]); }
                        items_ul.appendChild(item_li); }}}
            else{ // リスト作成済
                i_panel.style.display='block'; }

            panel_act();
            panel_get(); }

        else{
            let i_panel=document.querySelector('#i_panel');
            if(i_panel){
                i_panel.style.display='none'; }}

        function m(da){
            let d=da.toString();
            return ' '+ d.slice(0, 4) +'-'+ d.slice(4, 6) +'-'+ d.slice(6, 8); }
    } // disp_itemes()



    function panel_act(){ // 日付の近いリスト行を表示
        let entry_date=get_date_n();
        if(entry_date!=0){
            let next_index=pageDB.findIndex( item=> item[3] >= entry_date );
            if(next_index==-1){
                next_index=pageDB.length-1; }
            let list_items=document.querySelectorAll('.item_li');
            if(list_items.length>0){
                list_items[next_index-1].scrollIntoView({block: "center"}); }}}



    function panel_get(){
        let list_items=document.querySelectorAll('.item_li');
        for(let k=0; k<list_items.length; k++){
            list_items[k].onclick=function(){
                goto_(k); }}
        function goto_(k){
            goto(pageDB[k+1][0]); }}



    function goto(entry){
        let path=location.pathname; // 現在のパス名
        let user_id=path.split('/')[1];
        let goto_url=
            'https://ameblo.jp/'+ user_id +'/entry-'+ entry +'.html';
        window.location.href=goto_url; }



    function panel_color(index){
        let list_items=document.querySelectorAll('.item_li');
        if(list_items[index-1]){
            list_items[index-1].classList.toggle('raw');
            list_items[index-1].classList.toggle('dev'); }}



    let storage=document.querySelector('#storage');
    storage.onclick=function(){
        let conf_str=
            ' 🔴 全ての調査データを削除します\n\n'+
            '  調査データは 70kB/1000件で、ローカルストレージ\n'+
            '  の容量を心配する必要はありません。\n'+
            '  再調査の目的でデータをリセットする場合を除いて\n'+
            '  調査データを削除せずにしておく事をお勧めします。\n\n'+
            '  データを全削除する場合は「OK」を押してください';

        let ok=confirm(conf_str);
        if(ok){
            pageDB=[['', 0, 0, 0, 1]];
            let write_json=JSON.stringify(pageDB);
            localStorage.setItem('CKIMG_DB', write_json); // ローカルストレージ 保存
            window.location.reload(); }}

} // do_task()


 

〔追記〕2023.04

「secret.ameba.jp」のサブドメイン廃止に伴い、上記コードを修正しました。

ver.0.2 ➔ ver.0.3 に更新しています。