📰

Google ニュースのRSSから元記事のURLを手にいれる(実用には耐えない)

2024/12/14に公開

Google ニュースではRSS/Atomという2種類のフィードを参照できます。以下は「日経平均株価」というクエリに対するフィードです。

<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <script src="chrome-extension://hoklmmgfnpapgjgcpechhaamimifchmp/frame_ant/frame_ant.js" />
    <channel>
        <generator>NFE/5.0</generator>
        <title>"日経平均株価" - Google ニュース</title>
        <link>
            https://news.google.com/search?hl=ja&gl=JP&ceid=JP:ja&oc=11&q=%E6%97%A5%E7%B5%8C%E5%B9%B3%E5%9D%87%E6%A0%AA%E4%BE%A1</link>
        <language>ja</language>
        <webMaster>news-webmaster@google.com</webMaster>
        <copyright>Copyright © 2024 Google. All rights reserved. This XML feed is made available
            solely for the purpose of rendering Google News results within a personal feed reader
            for personal, non-commercial use. Any other use of the feed is expressly prohibited. By
            accessing this feed or using these results in any manner whatsoever, you agree to be
            bound by the foregoing restrictions.</copyright>
        <lastBuildDate>Sat, 14 Dec 2024 07:39:31 GMT</lastBuildDate>
        <image>
            <title>Google ニュース</title>
            <url>
                https://lh3.googleusercontent.com/-DR60l-K8vnyi99NZovm9HlXyZwQ85GMDxiwJWzoasZYCUrPuUM_P_4Rb7ei03j-0nRs0c4F=w256</url>
            <link>https://news.google.com/</link>
            <height>256</height>
            <width>256</width>
        </image>
        <description>Google ニュース</description>
        <item>
            <title>日経平均終値476円高 日銀無風観測で「幻のSQ」超え 越智小夏 - 日本経済新聞</title>
            <link>
                https://news.google.com/rss/articles/CBMibEFVX3lxTFBoeHE2bmtIYjhvbzhBMEVacWFxd2otcmw0VlFYTXhFMlNMRmJZNGxaN1k4T044LXNCcHZCa2FRUkxxMzlCRnFfQlA4SUZoV1lUaE1YYzdrYTFTb0pLcUJKOThacllndElscVJ1TA?oc=5</link>
            <guid isPermaLink="false">
                CBMibEFVX3lxTFBoeHE2bmtIYjhvbzhBMEVacWFxd2otcmw0VlFYTXhFMlNMRmJZNGxaN1k4T044LXNCcHZCa2FRUkxxMzlCRnFfQlA4SUZoV1lUaE1YYzdrYTFTb0pLcUJKOThacllndElscVJ1TA</guid>
            <pubDate>Thu, 12 Dec 2024 03:03:55 GMT</pubDate>
            <description><a
                    href="https://news.google.com/rss/articles/CBMibEFVX3lxTFBoeHE2bmtIYjhvbzhBMEVacWFxd2otcmw0VlFYTXhFMlNMRmJZNGxaN1k4T044LXNCcHZCa2FRUkxxMzlCRnFfQlA4SUZoV1lUaE1YYzdrYTFTb0pLcUJKOThacllndElscVJ1TA?oc=5"
                    target="_blank">日経平均終値476円高 日銀無風観測で「幻のSQ」超え 越智小夏</a>&nbsp;&nbsp;<font
                    color="#6f6f6f">日本経済新聞</font></description>
            <source url="https://www.nikkei.com">日本経済新聞</source>
        </item>
        <item>
            <title>【市況】 伊藤智洋が読む「日経平均株価・短期シナリオ」 (12月12日記) - 株探ニュース</title>
            <link>
                https://news.google.com/rss/articles/CBMiYEFVX3lxTFBEd1ZyV09tYWZNSDNkNUVMdFFZcmwwQWdGTnplSW0xVjh3RGdtSWFMVnRMemI0c010LVp4eUhFWC1pRklRalJBT2VKWkN4Y2NMSkJsVU5BNkZvaUV2QmxrbA?oc=5</link>
            <guid isPermaLink="false">
                CBMiYEFVX3lxTFBEd1ZyV09tYWZNSDNkNUVMdFFZcmwwQWdGTnplSW0xVjh3RGdtSWFMVnRMemI0c010LVp4eUhFWC1pRklRalJBT2VKWkN4Y2NMSkJsVU5BNkZvaUV2QmxrbA</guid>
            <pubDate>Wed, 11 Dec 2024 23:20:08 GMT</pubDate>
            <description><a
                    href="https://news.google.com/rss/articles/CBMiYEFVX3lxTFBEd1ZyV09tYWZNSDNkNUVMdFFZcmwwQWdGTnplSW0xVjh3RGdtSWFMVnRMemI0c010LVp4eUhFWC1pRklRalJBT2VKWkN4Y2NMSkJsVU5BNkZvaUV2QmxrbA?oc=5"
                    target="_blank">【市況】 伊藤智洋が読む「日経平均株価・短期シナリオ」 (12月12日記)</a>&nbsp;&nbsp;<font
                    color="#6f6f6f">株探ニュース</font></description>
            <source url="https://kabutan.jp">株探ニュース</source>
        </item>
        .....省略.....
    </channel>
</rss>

今回の問題点

RSSのlinkが https://news.google.com/rss/articles/CBMibEFVX... というように元記事のURLではなくGoogleが発行した中間ページへのURLになっています。
フィードをただfeedlyなどのRSSリーダーで読み込むだけなら問題ないのですが、フィードの情報を自分が開発しているサービスに組み込もうとすると以下のような問題が発生します。

  • 遷移する際にGoogleの中間ページを経由するので若干体験が落ちる
  • 元記事のOGP情報(og:imageog:description)を取得するためにスクレイピングする際に中間ページからの遷移をロジックに含める必要がある

1つ目はGoogleの処理はかなり早いのであまり問題にならないのですが、2つ目は大きめな問題でした。

情報の流れ

Google ニュースにおけるURLの処理は以下のような流れになっています。

Googleのサポートでも議論された履歴がありますが

https://support.google.com/news/publisher-center/thread/286847120/issue-with-final-url-retrieval-from-google-news-rss-feed?hl=en

どうやら以前は https://news.google.com/rss/articles/CBMibEFVX... というようなURLにアクセスすると元記事へリダイレクトしていたようです。
しかし、現在はGoogleのサーバでは中間ページまでしかリダイレクトは行わず、中間ページのjsの処理で元記事に遷移しているようです。

curlしてみても中間ページに遷移して200を返していることが確認できます。

$ curl -I -L "https://news.google.com/rss/articles/CBMibEFVX3lxTFBoeHE2bmtIYjhvbzhBMEVacWFxd2otcmw0VlFYTXhFMlNMRmJZNGxaN1k4T044LXNCcHZCa2FRUkxxMzlCRnFfQlA4SUZoV1lUaE1YYzdrYTFTb0pLcUJKOThacllndElscVJ1TA?oc=5"
HTTP/2 302 
content-type: application/binary
vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: Mon, 01 Jan 1990 00:00:00 GMT
date: Sat, 14 Dec 2024 08:08:00 GMT
location: https://news.google.com/rss/articles/CBMibEFVX3lxTFBoeHE2bmtIYjhvbzhBMEVacWFxd2otcmw0VlFYTXhFMlNMRmJZNGxaN1k4T044LXNCcHZCa2FRUkxxMzlCRnFfQlA4SUZoV1lUaE1YYzdrYTFTb0pLcUJKOThacllndElscVJ1TA?oc=5&hl=en-US&gl=US&ceid=US:en
content-length: 0
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
strict-transport-security: max-age=31536000
content-security-policy: script-src 'nonce-rqSbCX5ph0__LngvLpIk6A' 'unsafe-inline';object-src 'none';base-uri 'self';report-uri /_/DotsSplashUi/cspreport;worker-src 'self'
content-security-policy: require-trusted-types-for 'script';report-uri /_/DotsSplashUi/cspreport
cross-origin-resource-policy: same-site
permissions-policy: ch-ua-arch=*, ch-ua-bitness=*, ch-ua-full-version=*, ch-ua-full-version-list=*, ch-ua-model=*, ch-ua-wow64=*, ch-ua-form-factors=*, ch-ua-platform=*, ch-ua-platform-version=*
accept-ch: Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Model, Sec-CH-UA-WoW64, Sec-CH-UA-Form-Factors, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version
cross-origin-opener-policy: same-origin-allow-popups
server: ESF
x-xss-protection: 0
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
set-cookie: NID=520=AloRCeAQvQ-SqA5Vnpq7oMxoiZeotVkPz1wiQ41Gv0lAbGIKb5egoZFRlRHEcIe6QlssjVgtEpGOuayPc429mFi-_3SXBODOmibgKJCxPD_hN3iN4iL9zAJ2DyHsH9tudJTg99gzyDr91Cw49iond1EeuE7nOlgAPatt6n4R9Lg6KzLP6pc; expires=Sun, 15-Jun-2025 08:08:00 GMT; path=/; domain=.google.com; HttpOnly
set-cookie: GN_PREF=W251bGwsIkNBSVNDd2pnLWZTNkJoQ3dzXzAtIl0_; Expires=Sat, 14-Jun-2025 20:08:00 GMT; Path=/; Secure
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

HTTP/2 200 
content-type: text/html; charset=utf-8
vary: Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site
x-ua-compatible: IE=edge
cache-control: no-cache, no-store, max-age=0, must-revalidate
pragma: no-cache
expires: Mon, 01 Jan 1990 00:00:00 GMT
date: Sat, 14 Dec 2024 08:08:00 GMT
content-length: 572823
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
strict-transport-security: max-age=31536000
content-security-policy: script-src 'nonce-m6ymIsx5A9CKdB0xiYL1IQ' 'unsafe-inline';object-src 'none';base-uri 'self';report-uri /_/DotsSplashUi/cspreport;worker-src 'self'
content-security-policy: require-trusted-types-for 'script';report-uri /_/DotsSplashUi/cspreport
cross-origin-opener-policy: same-origin-allow-popups
accept-ch: Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Model, Sec-CH-UA-WoW64, Sec-CH-UA-Form-Factors, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version
cross-origin-resource-policy: same-site
permissions-policy: ch-ua-arch=*, ch-ua-bitness=*, ch-ua-full-version=*, ch-ua-full-version-list=*, ch-ua-model=*, ch-ua-wow64=*, ch-ua-form-factors=*, ch-ua-platform=*, ch-ua-platform-version=*
reporting-endpoints: default="/_/DotsSplashUi/web-reports?context=eJzjStHikmJw15BikPj6kkkDiJ3SZ7AGAfGnHTNYW2-eY50KxEZrz7M6AXHSv_OsRUDcUXqB1VDhEqsjEF9Ov8Sq2nOJ1RSImUwvs1rlX2YtkrjC2gTEvtpXWTt8r7IK8XA8-PllF5vAh_vTOpmUlJPyC-NT8kuKiwtyEoszilOLylKL4o0MjEwMjQwN9AwN4gsMADUhO0E"
server: ESF
x-xss-protection: 0
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
set-cookie: NID=520=Tyja_0uWauOU4HATQi_sr6TjGGwfDWlpgOsG51K2yFxoJOioMpMbj6uBPSheQljTQDE3MrNBkBJy4B-yJprWQfvknsaf70NhOXZ-R7OhKtCRxgIjDhnvWXsuqWbcLPsBXZYc7AY2lkvsM9OzgzYq4D7IUEhFD_6vydoF0nssD6N78N6yog; expires=Sun, 15-Jun-2025 08:08:00 GMT; path=/; domain=.google.com; HttpOnly
set-cookie: GN_PREF=W251bGwsIkNBSVNEQWpnLWZTNkJoQ1EwOHp1QVEiXQ__; Expires=Sat, 14-Jun-2025 20:08:00 GMT; Path=/; Secure
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

Googleが発行している中間ページから元記事のURLを得る

Googleの中間ページのURLはGoogleのDBに対する識別子でしかないので、base64デコードなどをしても元記事のURLは取得できません。
なので中間ページのjsの処理の中で元記事のURLを取得している処理を探す必要があります。

結論を言うと中間ページでは以下のURLに対してPOSTが行います。
https://news.google.com/_/DotsSplashUi/data/batchexecute?rpcids=Fbv4je&source-path=%2Frss%2Farticles%2FCBMibEFVX3lxTFBoeHE2bmtIYjhvbzhBMEVacWFxd2otcmw0VlFYTXhFMlNMRmJZNGxaN1k4T044LXNCcHZCa2FRUkxxMzlCRnFfQlA4SUZoV1lUaE1YYzdrYTFTb0pLcUJKOThacllndElscVJ1TA&f.sid=2653858769207096270&bl=boq_dotssplashserver_20241210.10_p0&hl=ja&gl=JP&soc-app=140&soc-platform=1&soc-device=1&_reqid=62813&rt=c

結果として↓のレスポンスが返ってきます。この中に元記事のURLが含まれているのでそれを抽出することで元記事のURLを取得できます。

)]}'

[["wrb.fr","Fbv4je","[\"garturlres\",\"https://kabutan.jp/news/marketnews/?b\\u003dn202412120139\",1]",null,null,null,""],["di",12],["af.httprm",12,"8979175738061918952",0]]

上記のロジックをPythonのコードに落とし込んだものがあるので具体的なコードは以下を参照すると良いです。
https://github.com/SSujitX/google-news-url-decoder

実用に耐えない点

ここまででGoogle ニュースのRSSから元記事のURLを取得することができるようになったのですが、バッチ処理を組むと問題が発生します。

https://news.google.com/_/DotsSplashUi/data/batchexecute?... のURLに対するアクセスを100回ほどすると429エラーになりIPがブロックされてしまうことです。
Google ニュースのRSSは100件あるのでとても実用に耐えませんでした。。。

GitHubで編集を提案

Discussion