「半角空白」が検索できない場合がある問題

「ver.0.5」のチェックをしていると、運良く前ページに例があったので気付く事ができました。 これまでにも何度か問題になった「 」(ノーブレイクスペース)が、検索にかからなかったのです。

 

「 」は、アメーバのPC編集画面で記事を書いていると、比較的頻繁に書き込まれます。「通常表示」から見ると普通の「半角空白」と同じで、ユーザーは単なる空白と思ってしまいますが、文字コードが違います。「HTML表示」で見ると下の様に違いがはっきりします。

 

 

 

とりたてて「半角空白」に訂正する必要はないのですが、今回の様に「 」が問題になる事があります。

 

それでは「 」を書けと言われても「?」となりますね。「Abema」「 TV」と2行に書き、TVの行頭で「BackSpace」を押すと「 」が間に入ります。 空白の改行は「 」だらけなので、ちょっとした編集操作で紛れ込むのでしょう。

 

で、「ver.0.5」までの検索コードでは、「半角空白」「全角空白」「 」は検索上で別物として扱われます。 これは、検索機能として理想的なのですが、実用上で「半角空白」に見えるものは同じ物として扱えないと困るわけです。

 

前ページの「Abema TV」に「 」が混ざっていたので、「厳密な検索」で検索語「Abema TV」を指定すると、ヒット数が合いません。 最初は理由が判らず焦りました。

 

 

対策 

「空白が混ざった文字列」を検索できる事は大きな強みですから、絶対に譲れない問題です。 ネットには「半角空白」「 」の両方を検索するには「空白文字」を「\s」に置換えるという情報があります。 これを最初に試したのですが、確かにそれで「 」にもヒットする様になりますが、今度は「全角空白」がヒットしてしまいます。「半角空白」「全角空白」の区別も出来なくなるのです。

 

ここで再び色々なページを当たり、「半角空白」「 」をそれぞれの文字コードに置き換えて検索するという方法に辿り着きました。

 

▪「半角空白」の文字コード(unicode)は「u0020」 文字列上では「\u0020」

▪「 」の文字コード(unicode)は「u00A0」 文字列上では「\u00A0」

 

「A」と「B」の両方にヒットする正規表現の書き方は「 (A|B) 」ですから、これに当てはめた書式を作りました。「 ( | ) 」をエスケープする「\」を付けています。

 

\(\u0020\|\u00A0\)

 

検索語に「半角空白」があれば、上の書式に置き換えて検索を行う様にしました。

以下は、エスケープ処理の後に、この置換え処理を追加したコードです。

 

「Ameba Search Tools」 ver.0.6   376行~

function escape_exs(string){
    let reRegExp=/[\\^$.*+?()[\]{}|]/g;
    let reHasRegExp=new RegExp(reRegExp.source);
    string=
        (string && reHasRegExp.test(string)) ? string.replace(reRegExp, '\\$&') : string;
    string=string.replace(' ', '\(\u0020\|\u00A0\)');
    return string; }

 

▪ 検索窓に書き込まれる「検索語」は「半角空白」しか受付けない様で、「半角空白」の入力で「半角空白」「 」の両方にヒットさせるコードにしています。

 

▪ 文字コードのエスケープは「\\u~」が正しいと思えるのですが、テストすると「\u~」でないと動作しません。 正規表現は難しいです。

 

 

実証テスト 

下は、「半角空白」「 」「全角空白」を含んだ「ABEMA」のサンプルです。

検索ヒットの違いが判る様に、サンプル数を変えています。

 

① ABEMA TV (半角空白)

 

➁ ABEMA TV ABEMA TV (&nbsp)

 

➂ ABEMA TV ABEMA TV ABEMA TV  (全角空白)

 

 

 

 ①「検索語」➂「除外語」の指定で「厳密な検索」を実証テストしてみました。

 

「ver.0.5」の結果 

① を検索できますが ➁の「 」を検索できません。

また全角空白と半角空白は区別できています。

 

 

 

「ver.0.6」の結果 

①「半角空白」と ➁「 」を検索して、検索語のヒットが「3」です。

また全角空白と半角空白は区別できています。

 

 

 

これで「 」が混入している場合も、問題なく検索が行えます。

 

 

 

 「Ameba Search Tools」の操作マニュアル

操作マニュアルは以下のページにあります。

 

 

 

 

「Ameba Search Tools」を利用するには

このツールは Chrome / Edge / Firefox版の拡張機能「Tampermonkey」上で動作します。 以下に、このツールの導入手順を簡単に説明します。

 

❶「Tampermonkey」を導入します

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

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

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

 

 

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

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

 

 

 

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

 

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

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

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

 

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

 

 

〔 Ameba Search Tools 〕 ver. 0.6

 

// ==UserScript==
// @name         Ameba Search Tools
// @namespace    http://tampermonkey.net/
// @version      0.6
// @description  アメーバ検索の環境ツール+厳密な記事検索
// @author      Ameba Blog User
// @match       https://ameblo.jp/*
// @match       https://search.ameba.jp/*
// @icon          https://www.google.com/s2/favicons?sz=64&domain=ameblo.jp
// @grant        none
// ==/UserScript==



// 以下は検索ページで動作 ==========================

if(window.location.hostname=='search.ameba.jp'){


    let target=document.querySelector('head title');
    let monitor=new MutationObserver(page_select);
    monitor.observe(target, { childList: true });

    page_select();

    function page_select(){
        if(document.querySelector('.PcResultPagination')){ // 検索ページャーのある画面でのみ動作

            let now;
            let now_u;
            let now_s;
            let q;
            let now_s_q;
            let now_s_pre;
            let now_s_next;
            let pre_url;
            let next_url;


            function get_pre_url(){
                now=location.href;
                now_u=now.split('?')[0];
                now_s=now.split('?')[1];

                if(now_s){
                    now_s=now_s.replace('&sbs=0', '');
                    q=now_s.split('&');
                    for(let k=0; k<q.length; k++){
                        if(q[k].indexOf('p=')!=-1){
                            now_s_q=q[k]; }}

                    let now_page;
                    if(now_s_q){
                        now_page=now_s_q.substr(2);

                        if(now_page/1==1){
                            now_s_pre='p=1'; }
                        else{
                            now_s_pre='p='+ (now_page/1 -1); }
                        if(now_s_pre){
                            for(let k=0; k<q.length; k++){
                                if(q[k].indexOf('p=')!=-1){
                                    q[k]=now_s_pre; }}
                            pre_url=now_u +'?'+ q.join('&'); }}
                    else{
                        pre_url=now +'&p=1'; }}
                else{
                    pre_url=now +'?p=1'; }

            } // get_pre_url()


            function get_next_url(){
                now=location.href;
                now_u=now.split('?')[0];
                now_s=now.split('?')[1];

                if(now_s){
                    now_s=now_s.replace('&sbs=0', '');
                    q=now_s.split('&');
                    for(let k=0; k<q.length; k++){
                        if(q[k].indexOf('p=')!=-1){
                            now_s_q=q[k]; }}

                    let now_page;
                    if(now_s_q){
                        now_page=now_s_q.substr(2);

                        if(now_page/1==100){
                            now_s_next='p=100'; }
                        else{
                            now_s_next='p='+ (now_page/1 +1); }
                        if(now_s_next){
                            for(let k=0; k<q.length; k++){
                                if(q[k].indexOf('p=')!=-1){
                                    q[k]=now_s_next; }}
                            next_url=now_u +'?'+ q.join('&'); }}
                    else{
                        next_url=now +'&p=1'; }}
                else{
                    next_url=now +'?p=1'; }

            } // get_next_url()



            document.addEventListener("keydown", check_arrow);
            function check_arrow(event){
                if(event.keyCode==37){ //「⇦」
                    if(is_able()){
                        event.preventDefault();
                        go_pre(); }}
                if(event.keyCode==39){ //「⇨」
                    if(is_able()){
                        event.preventDefault();
                        go_next(); }}
                if(event.keyCode==38){ //「⇧」
                    if(is_able()){
                        event.preventDefault();
                        select_up(); }}
                if(event.keyCode==40){ //「⇩」
                    if(is_able()){
                        event.preventDefault();
                        select_down(); }}
                if(event.keyCode==13){ //「Enter」
                    event.preventDefault();
                    select_open(event); }
                if(event.keyCode==32){ //「Space」
                    if(is_able()){
                        event.preventDefault();
                        severe_search(); }}
                if(event.keyCode==27){ //「ESC」
                    if(is_able()){
                        clear_select(); }}}


            function go_pre(){
                if(go_check(0)){
                    get_pre_url();
                    if(pre_url){
                        location.href=pre_url; }}}


            function go_next(){
                if(go_check(1)){
                    get_next_url();
                    if(next_url){
                        location.href=next_url; }}}


            function go_check(n){
                if(n==0){
                    let edge=document.querySelector('.PcResultPagination_MoreLink .s-triangle-left');
                    if(edge){
                        return true; }
                    else{
                        return false; }}
                if(n==1){
                    let edge=document.querySelector('.PcResultPagination_MoreLink .s-triangle-right');
                    if(edge){
                        return true; }
                    else{
                        return false; }}}


            function go_check2(url){
                if(url.includes('/news/')){
                    return false; }
                else{
                    return true; }}

            let order=-1;

            function select_up(){
                let items=get_items();
                if(order>0){
                    for(let k=0; k<items.length; k++){
                        items[k].style.outline=''; }
                    order=order-1;
                    items[order].style.outline='2px solid red'; }}


            function select_down(){
                let items=get_items();
                if(order<items.length-1){
                    for(let k=0; k<items.length; k++){
                        items[k].style.outline=''; }
                    order=order+1;
                    items[order].style.outline='2px solid red'; }}


            function select_open(event){
                let items=get_items();
                for(let k=0; k<items.length; k++){
                    if(items[k].style.outlineWidth=='2px'){
                        let link=items[k].querySelector('a');
                        if(link){
                            let url=link.getAttribute('href');
                            if(event.ctrlKey){
                                window.open(url, '_blank'); }
                            else{
                                window.location.href=url; }}}}}


            let s_input=document.querySelector('.PcSuggestForm_Input');
            if(s_input){
                s_input.onclick=function(){
                    clear_select(); }}


            function clear_select(){
                let items=get_items();
                for(let k=0; k<items.length; k++){
                    items[k].style.outline=''; }}


            function is_able(){
                let s_input=document.querySelector('.PcSuggestForm_Input');
                if(s_input && s_input==document.activeElement){
                    return false; }
                else{
                    return true; }}


            function get_items(){
                let PcBL=document.querySelectorAll('.PcBloggerListItem');
                let PcAN=document.querySelectorAll('.PcAmebaNewsListItem');
                let PcEL=document.querySelectorAll('.PcEntryListItem');

                if(PcBL.length!=0){
                    return PcBL; }
                else if(PcAN.length!=0){
                    return PcAN; }
                else if(PcEL.length!=0){
                    return PcEL; }
            } // get_items()



            function severe_search(){
                let ok=confirm("💢 このページの全記事の厳密な検索をします");
                if(ok){
                    let items=get_page_items();
                    for(let k=0; k<items.length; k++){
                        let link=items[k].querySelector('a');
                        if(link){
                            let url=link.getAttribute('href');
                            window.open(url+'?sbs', '_blank'); }}}


                function get_page_items(){
                    let PcBL=document.querySelectorAll('.PcBloggerListItem');
                    let PcEL=document.querySelectorAll('.PcEntryListItem');

                    if(PcBL.length!=0){
                        return PcBL; }
                    else if(PcEL.length!=0){
                        return PcEL; }
                } // get_page_items()

            } // severe_search()


            let pagination=document.querySelector('.PcResultPagination');
            if(pagination){
                let help=
                    '<div id="astool_help">'+
                    '<div class="upper">'+
                    '⇦ ⇨:リスト移動<br>'+
                    '⇩ ⇧:記事の選択<br>'+
                    'Enter :選択記事へ<br>'+
                    'Ctrl+Enter:別タブ<br>'+
                    'Ctrl+Tab:タブ移動<br>'+
                    'Space:顕密な検索<br>'+
                    '</div>'+
                    '<p class="lower">Search Tools '+
                    '<a href="https://ameblo.jp/personwritep/entry-12845478988.html" '+
                    'rel="noopener noreferrer" target="_blank">'+
                    '<span class="help">?</span></a></p>'+
                    '<style>'+
                    '#astool_help { position: absolute; top: -4px; left: -180px; '+
                    'font: normal 14px Meiryo; text-align: left; width: 138px; '+
                    'border: 1px solid #aaa; background: #fff; } '+
                    '.upper { padding: 4px 0 4px 5px; } '+
                    '.lower { padding: 2px 2px 0 10px; color: #fff; background: #bcbfc3; } '+
                    '.help { display: inline-block; height: 17px; padding: 1px 2px 2px 2px; '+
                    'font: bold 16px/21px Meiryo; color: #fff; '+
                    'border-radius: 30px; background: #666; cursor: pointer; }'+
                    '</style></div>';

                if(!pagination.querySelector('#astool_help')){
                    pagination.insertAdjacentHTML('afterbegin', help); }}

        } // if('.PcResultPagination')
    } // page_select()

} // if('search.ameba.jp')





// 以下は個別ブログページで動作 ==========================

if(window.location.hostname=='ameblo.jp'){

    let word;
    let read_json=localStorage.getItem('SBS'); // ローカルストレージ保存名
    word=JSON.parse(read_json);
    if(word==null || word.length!=5){
        word=['','','','', 0]; } // 0検索語/1除外語/2除外語/3除外語/4パネル幅



    let retry=0;
    let interval=setInterval(wait_target, 500);
    function wait_target(){
        retry++;
        if(retry>8){ // 制限 4sec
            clearInterval(interval);
            main_sub(); }
        let entrybody=document.querySelector('#entryBody'); // 監視 target
        if(entrybody){
            clearInterval(interval);
            main(); }}



    function main_sub(){ // アメンバー記事や記事ページでない場合
        if(window.location.search=='?sbs'){
            window.close(); }}



    function main(){

        function call_h(){
            let header;
            if(document.querySelector('#app header')){
                header=document.querySelector('#app header'); } // 新タイプスキン・旧タイプスキン
            else if(document.querySelector('#app #header')){
                header=document.querySelector('#app #header'); } // レトロタイプスキン
            return header; }


        function call_t(){
            let title;
            let article=document.querySelector('.js-entryWrapper'); //記事全体
            if(article){
                if(article.querySelector('h1')){
                    title=article.querySelector('h1'); } // 新タイプスキン・旧タイプスキン
                else if(article.querySelector('h3')){
                    title=article.querySelector('h3'); }} // レトロタイプスキン
            return title; }


        let entrybody=document.querySelector('#entryBody');
        if(entrybody){ // リスト・タイルのトップページ、アメンバーページ 等は検索しない
            let count0;
            let count1;
            let count2;
            let count3;

            count_do();

            function count_do(){
                let entrybody=document.querySelector('#entryBody');
                let page_text=entrybody.textContent;

                if(call_h()){
                    page_text+=call_h().textContent; }
                if(call_t()){
                    page_text+=call_t().textContent; }

                if(word[0]!=''){
                    let word0_es=escape_exs(word[0]);
                    count0=(page_text.match(new RegExp(word0_es, 'g')) || []).length; }
                else{
                    count0=0; }
                if(word[1]!=''){
                    let word1_es=escape_exs(word[1]);
                    count1=(page_text.match(new RegExp(word1_es, 'g')) || []).length; }
                else{
                    count1=0; }
                if(word[2]!=''){
                    let word2_es=escape_exs(word[2]);
                    count2=(page_text.match(new RegExp(word2_es, 'g')) || []).length; }
                else{
                    count2=0; }
                if(word[3]!=''){
                    let word3_es=escape_exs(word[3]);
                    count3=(page_text.match(new RegExp(word3_es, 'g')) || []).length; }
                else{
                    count3=0; }


                function escape_exs(string){
                    let reRegExp=/[\\^$.*+?()[\]{}|]/g;
                    let reHasRegExp=new RegExp(reRegExp.source);
                    string=
                        (string && reHasRegExp.test(string)) ? string.replace(reRegExp, '\\$&') : string;
                    string=string.replace(' ', '\(\u0020\|\u00A0\)');
                    return string; }

            } // count_do()



            if(window.location.search=='?sbs'){
                if(count0==count1+count2+count3){
                    window.close(); }} // 手動で開いたページでは閉じず、スクリプトで開いた場合は閉じる



            let set_word_box=
                '<div id="set_word_box">'+
                '検索:<input id="w_set0" class="w_set" type="text">'+
                '<span id="result0" class="result"> </span>'+
                '除外:<input id="w_set1" class="w_set" type="text">'+
                '<span id="result1" class="result"> </span>'+
                '<input id="w_set2" class="w_set" type="text">'+
                '<span id="result2" class="result"> </span>'+
                '<input id="w_set3" class="w_set" type="text">'+
                '<span id="result3" class="result"> </span>'+
                '<input id="set" type="button" value="Set">'+
                '<input id="hide" type="button" value="◁">'+
                '</div>'+
                '<style>'+
                '#set_word_box { display: flex; align-items: center; justify-content: flex-end; '+
                'position: fixed; top: 60px; left: 20px; padding: 6px; font: normal 16px Meiryo; '+
                'color: #000; overflow: hidden; white-space: nowrap; border: 1px solid #aaa; '+
                'background: #e6f3f9; box-shadow: 10px 20px 50px rgb(0 0 0 /20%); z-index: 9; } '+
                '#set_word_box input {font: normal 16px Meiryo; } '+
                '.w_set { width: 120px; height: 24px; padding: 4px 6px 2px; margin-right: 4px; } '+
                '#w_set1, #w_set2, #w_set3 { filter: brightness(0.97); } '+
                '.result { display: inline-block; min-width: 12px; padding: 4px 4px 2px; height: 26px; '+
                'margin-right: 12px; text-align: center; border: 1px solid #666; border-radius: 2px; } '+
                '#result0 { margin-right: 25px; background: #fff; } '+
                '#set { padding: 4px 4px 2px; margin-left: 20px; } '+
                '#hide { padding: 4px 4px 2px; margin-left: 10px; } '+
                '</style>';

            if(!document.querySelector('#set_word_box')){
                document.body.insertAdjacentHTML('beforeend', set_word_box); }


            disp_count();
            panel_width();



            function disp_count(){
                let w_set0=document.querySelector('#w_set0');
                let w_set1=document.querySelector('#w_set1');
                let w_set2=document.querySelector('#w_set2');
                let w_set3=document.querySelector('#w_set3');
                w_set0.value=word[0];
                w_set1.value=word[1];
                w_set2.value=word[2];
                w_set3.value=word[3];
                let result0=document.querySelector('#result0');
                let result1=document.querySelector('#result1');
                let result2=document.querySelector('#result2');
                let result3=document.querySelector('#result3');

                result0.textContent=count0.toString();
                result1.textContent=count1.toString();
                result2.textContent=count2.toString();
                result3.textContent=count3.toString(); }



            let set=document.querySelector('#set');
            set.onclick=function(){
                let w_set0=document.querySelector('#w_set0');
                let w_set1=document.querySelector('#w_set1');
                let w_set2=document.querySelector('#w_set2');
                let w_set3=document.querySelector('#w_set3');
                word[0]=w_set0.value;
                word[1]=w_set1.value;
                word[2]=w_set2.value;
                word[3]=w_set3.value;

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

                count_do(); // ページの再検索
                disp_count();

                alert("「検索語」と「検索結果から排除する検索語」を登録しました"); }



            let target=document.querySelector('head title');
            let monitor=new MutationObserver(page_count);
            monitor.observe(target, { childList: true });

            function page_count(){
                count_do();
                disp_count(); }



            let hide=document.querySelector('#hide');
            hide.onclick=function(){
                if(word[4]==0){
                    word[4]=1;
                    let write_json=JSON.stringify(word);
                    localStorage.setItem('SBS', write_json); } // ローカルストレージ保存
                else{
                    word[4]=0;
                    let write_json=JSON.stringify(word);
                    localStorage.setItem('SBS', write_json); } // ローカルストレージ保存
                panel_width(); }


            function panel_width(){
                let set_box=document.querySelector('#set_word_box');
                let hide=document.querySelector('#hide');
                if(word[4]==0){
                    set_box.style.width='';
                    hide.value='◁'; }
                else{
                    set_box.style.width='28px';
                    hide.value='▷'; }}

        } // if(entrybody)

    } // main()

} // if('ameblo.jp')



 

 

 

アメーバ検索のページデザインについて 

このページの「アメーバ検索画面」のスクリーンショットは、「Stylus」を使ったアレンジを適用しています。 以下のアレンジは、検索画面を大変に使い易くします。

 

 

 

これらのアレンジを検索画面に適用するには、拡張機能「Stylus」の導入が必要です。「Stylus」の導入やスタイルの取得については、以下のページを参照ください。

 

 

 

 

「Ameba Search Tools」最新版について 

旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。

 

●「Ameba Search Tools」の最新バージョンへのリンクは、以下のページのリンクリストから探せます。