Browse Source

fix: 使用 curl 浏览器请求头获取官网日志

Hilda-Bot 2 days ago
parent
commit
0e31cb4c23
2 changed files with 30 additions and 2 deletions
  1. 20 2
      Actions/translation/pipeline.py
  2. 10 0
      Actions/translation/test_pipeline.py

+ 20 - 2
Actions/translation/pipeline.py

@@ -22,6 +22,24 @@ def fetch(url):
         return response.read().decode('utf-8')
         return response.read().decode('utf-8')
 
 
 
 
+def fetch_official(url):
+    headers = {
+        'Upgrade-Insecure-Requests': '1',
+        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/157.0.0.0 Safari/537.36 Edg/157.0.0.0',
+        'sec-ch-ua': '\"Not-A.Brand\";v=\"99\", \"Microsoft Edge\";v=\"157\", \"Chromium\";v=\"157\"',
+        'sec-ch-ua-mobile': '?0',
+        'sec-ch-ua-platform': '\"macOS\"',
+    }
+    command = ['curl', '--url', url, '--location', '--fail', '--silent', '--show-error',
+               '--connect-timeout', '15', '--max-time', '45']
+    for name, value in headers.items():
+        command.extend(['-H', f'{name}: {value}'])
+    html = subprocess.check_output(command, text=True, timeout=50)
+    if 'MC_Layout_Article' not in html:
+        raise RuntimeError('Official response is not a supported article page')
+    return html
+
+
 def version_info(vid):
 def version_info(vid):
     if re.fullmatch(r'\d+(?:\.\d+){1,2}', vid):
     if re.fullmatch(r'\d+(?:\.\d+){1,2}', vid):
         base, kind, slug = vid, 'release', 'java-edition-' + vid.replace('.', '-')
         base, kind, slug = vid, 'release', 'java-edition-' + vid.replace('.', '-')
@@ -85,7 +103,7 @@ def prepare(days):
                 continue  # Never overwrite existing completed/manual translations.
                 continue  # Never overwrite existing completed/manual translations.
             folder = WORK / vid
             folder = WORK / vid
             folder.mkdir(exist_ok=True)
             folder.mkdir(exist_ok=True)
-            html = fetch(official)
+            html = fetch_official(official)
             (folder/'official.html').write_text(html)
             (folder/'official.html').write_text(html)
             source = WORK/'spxx'
             source = WORK/'spxx'
             if not source.exists():
             if not source.exists():
@@ -93,7 +111,7 @@ def prepare(days):
             subprocess.run(['git','-C',str(source),'checkout','--detach',SPXX_SHA],check=True)
             subprocess.run(['git','-C',str(source),'checkout','--detach',SPXX_SHA],check=True)
             subprocess.run(['node',str(Path(__file__).with_name('convert.mjs')),str(source),str(folder/'official.html'),official,str(folder/'source.md')],check=True)
             subprocess.run(['node',str(Path(__file__).with_name('convert.mjs')),str(source),str(folder/'official.html'),official,str(folder/'source.md')],check=True)
             title = 'Java版' + vid
             title = 'Java版' + vid
-            api = 'https://zh.minecraft.wiki/api.php?' + urllib.parse.urlencode({'action':'parse','page':title,'prop':'wikitext','format':'json','redirects':1})
+            api = 'https://zh.minecraft.wiki/api.php?' + urllib.parse.urlencode({'action':'parse','page':title,'prop':'wikitext|revid','format':'json','redirects':1})
             wiki = json.loads(fetch(api))
             wiki = json.loads(fetch(api))
             if 'error' in wiki:
             if 'error' in wiki:
                 raise RuntimeError('Chinese Wiki page unavailable; retry next run')
                 raise RuntimeError('Chinese Wiki page unavailable; retry next run')

+ 10 - 0
Actions/translation/test_pipeline.py

@@ -15,6 +15,16 @@ class PipelineTests(unittest.TestCase):
             with self.assertRaises(RuntimeError): p.sync_after_detection()
             with self.assertRaises(RuntimeError): p.sync_after_detection()
             run.assert_not_called()
             run.assert_not_called()
 
 
+    def test_official_curl_headers_and_error_page(self):
+        with patch.object(p.subprocess, 'check_output', return_value='<div class="MC_Layout_Article"></div>') as run:
+            p.fetch_official('https://www.minecraft.net/en-us/article/test')
+            command = run.call_args.args[0]
+            self.assertEqual(command[0], 'curl')
+            self.assertIn('sec-ch-ua-mobile: ?0', command)
+            self.assertIn('--fail', command)
+        with patch.object(p.subprocess, 'check_output', return_value='<html>Access Denied</html>'):
+            with self.assertRaises(RuntimeError): p.fetch_official('https://www.minecraft.net/en-us/article/test')
+
     def test_paths(self):
     def test_paths(self):
         cases = {'26.3-snapshot-2':'26/26.3/snapshot/26.3-snapshot-2.md',
         cases = {'26.3-snapshot-2':'26/26.3/snapshot/26.3-snapshot-2.md',
                  '1.21.11-pre1':'1.21/1.21.11/pre-release/1.21.11-pre1.md',
                  '1.21.11-pre1':'1.21/1.21.11/pre-release/1.21.11-pre1.md',