python - 无法与 API 通信
问题描述
我正在尝试抓取以下网站
我注意到在页面之间导航期间对以下端点进行了 XHR POST 请求,如您在以下打印屏幕中所见:
卡在 POST 请求中的东西,我注意到后面有一个动态值,GBK-
但我不明白它是从哪里生成的或如何获取它。
如果您只是在页面之间保持导航,您会注意到值保持变化。
根据以下答案的更新Life is complex
:
这是向 API 发送POST
请求的方法:
import requests
# we need the value!
url = "http://app1.nmpa.gov.cn/data_nmpa/face3/search.jsp?6SQk6G2z=GBK-Value"
# here you can add headers as you need!
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"cache-control": "no-cache",
"Content-Type": "application/x-www-form-urlencoded",
"Pragma": "no-cache"
}
data = {
"tableId": "27",
"State": [
"1",
"1",
"1",
"1",
"1",
"1",
"1"
],
"bcId": "152904442584853439006654836900",
"tableName": "TABLE27",
"viewtitleName": "COLUMN200",
"viewsubTitleName": "COLUMN192,COLUMN199",
"curstart": "2", # here is the Page number
"tableView": "进口医疗器械产品(注冿"
}
r = requests.post(url, headers=headers, data=data)
解决方案
结论 - 2021 年 7 月 25 日
在更详细地研究了这个问题之后,我认为使用 PythonRequests
来抓取您问题中的网站和表格在技术上是不可能的。
这意味着您的问题无法以您希望的方式解决。
为什么?
该网站采用反刮擦机制。
这些GBK
值只是这些机制的一部分。您尝试抓取的表格有 1504 页。GBK
每次从第 1 页导航到下一页等时,都会创建一个新的唯一值。因此有 1503 个唯一GBK
值。
该站点还为每个页面使用唯一的会话管理 cookie。
# page 1 cookie
JSESSIONID=0AC56294FE6857A236F0E68A9106E1AE.7; neCYtZEjo8GmS=5El51n08q7nzOG_bzzqhGWyfW_Lx9tCv1uZA6QjBcUq0rH0d1XYIvTKzN3MfNn2cZasqfZoM8Yo5NTpuq9gM.IG; neCYtZEjo8GmT=53HTPEbke3aQqqqm_6QLwIaUKu0tMygss.En464jhvNz1mMzbOatzmLLtv9x_xiCP6JaO_JzcbvHqtsnQYydBa6B_YjSg6sFm7cVBBOhB35_.TZuwDsbOnDinJkNwMs3AaMPtM83dP9YnogFKHpNJo5.RHMTKT6_XNPr0mxebR6stRrQ7LFfACcWqHHhbc.j6gZfZzxsgwnPE3RGP6aT9nYuMJbvK2EGrdAv0O12G03KTk_BMk.xLeEwrQq5VjyH1tB7t4wQ.jQ1geshvbDPCs8_VHCkd2.6uIag5Md.lngzeDshhSjMrmBjyy0HTqAXQ3; acw_tc=276aedd816272186939626726e424a5dd554d4b095225e2cac90fc6d2da583
# page 2 cookie
JSESSIONID=651AD12FD349FFB1842E08CA578EA37D.7; neCYtZEjo8GmS=5El51n08q7nzOG_bzzqhGWyfW_Lx9tCv1uZA6QjBcUq0rH0d1XYIvTKzN3MfNn2cZasqfZoM8Yo5NTpuq9gM.IG; neCYtZEjo8GmT=53HTPeKke3e7qqqm_6Q_YEqK9dBPNnJQF00YvHDMLHlJeb.4rrpTsgfwZxU0S5OXIAB2aduoOTmj7RuKIL.LUXRaRqfh5ZByuTFX3LxK1Ia3sr3V45c.PPx6Eas5EF5EkQztquzrX78QIbjrJUcQoKoOKcqgX5UuRIN0gCyGDyI6FFj.JbPhwYf65Hcx9BzDQnrlGAPHM3WGvmKf7OJnLY1SGIuxtdyVUE359Ll2lr0QJxUq1Dacqz_WsFa_ZantBbP7MklHX6J21wmDnyo6s4xCeeTYwsGq.kGUbE74Dx.QjQBCM_SiLKccTog8_EdBDg; acw_tc=276aedd816272186939626726e424a5dd554d4b095225e2cac90fc6d2da583
# page 3 cookie
JSESSIONID=2121D74E0EFCEC3BE104DAA2791481B6.7; neCYtZEjo8GmS=5El51n08q7nzOG_bzzqhGWyfW_Lx9tCv1uZA6QjBcUq0rH0d1XYIvTKzN3MfNn2cZasqfZoM8Yo5NTpuq9gM.IG; neCYtZEjo8GmT=53HTPeKke3e7qqqm_6wBfEGBZsTF9_uGtgepzPXNOzFh0RNtGcE1Cf4hEQNppVywcI5mk3SlLkzvNll6ovr4XmfL2Ujy3AFZR5leVY2H3_584At3GmIwmnsEjOx5v5e_lMon3AbX9t2W8UiLoK.9SBX0vgNRfkqdpyPjWKk3Zs8gQG0k3_6UwxGTvEwWkaWL8vquJgCGlvLEFTjNvd07eHiR482UfpLPFP6yAkx8Wi9pM79cL.26KE3U2L79hgBKLHyOdNyj3VKOkDsaXefNdPXd.YqT4kevShGxzMM2PuzqnuuQnW.GQ5mr9Rx8VxUjEa; acw_tc=276aedd816272186939626726e424a5dd554d4b095225e2cac90fc6d2da583
因此,您需要为GBK
2 到 1504 之间的每个页面获取唯一值和唯一会话管理 cookie。
我还注意到该站点采用某种类型的延迟。第一个页面可能需要一些时间才能完全加载。如果您尝试导航到另一个页面,此页面加载完成,您将收到此消息“在此操作之前!”
加载某些页面最多需要 2 分钟,这毫无价值。当他们没有加载上面的消息时显示。
就像我之前提到的,你应该考虑抓取这个站点,selenium,
它可能会绕过反抓取机制。
更新后(硒)- 2021 年 7 月 27 日
我试图用selenium
. chromedriver
不断无法连接到该站点。即使使用了这些开关:
chrome_options.add_experimental_option("useAutomationExtension", False)
chrome_options.add_experimental_option("excludeSwitches", ['enable-automation'])
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
我也尝试使用undetected-chromedriver,但这也无法进行属性连接。
即使我设置了一个较长的超时时间,或者driver.set_page_load_timeout()
会话driver.implicitly_wait()
仍然以chromedriver
.
selenium
当我使用geckodriver.
There are still time out issues时,我能够访问该网站,但添加一WebDriverWait
对expected_conditions
似乎可以克服一些超时问题。
由于使用了反抓取机制,因此抓取该网站将是一个漫长而艰巨的过程。
原帖 - 2021 年 7 月 24 日
首先让我说,您的问题没有足够的细节来为解决您的问题提出可靠的建议。
我调查了你的问题。我发现您的目标网站使用一些 Javascript 来动态创建GBK
每个发布请求使用的值。
6SQk6G2z:GBK-5lkb7acLMDDxywZsCHoJagJlT50f1gw4.jaVgaBpBcGZDs1T_pcR_OPFgvOm_6oM8PfyL3L6xDPxFqgIqgwbVAEw8y4jd0P5yTWo3dx1cNLnCOYTa4mVr7azAXa9YiDEhOz7M1Qsw6BJIOSq0QVp.Ng.NWri7ByAK6dwme99ZEOnjraxZex1xLVGakyVVCoOEhFGfphV8D1GDFKLt1dG.4_XuCPDIoLNGmy4Dzd92SxlNWCQ707A8tvqP7jQq2wyRBV0M3y0moSs8I03rIXeYNKE3AkMmI8Xp4M6GZd0seJqGvGrN7vA8lJbiBfmEgtcSvPZF0hrfkVRvQGq9uHRx9JOLtdkujsYHk6TW7rYBVsQ
在页面 1 到 1504 之间导航时使用此GBK
值。我注意到每个页面的值都会发生变化。
import difflib
# page 2
a = "6SQk6G2z:GBK-5lkb7acLMDDxywZsCHoJagJlT50f1gw4.jaVgaBpBcGZDs1T_pcR_OPFgvOm_6oM8PfyL3L6xDPxFqgIqgwbVAEw8y4jd0P5yTWo3dx1cNLnCOYTa4mVr7azAXa9YiDEhOz7M1Qsw6BJIOSq0QVp.Ng.NWri7ByAK6dwme99ZEOnjraxZex1xLVGakyVVCoOEhFGfphV8D1GDFKLt1dG.4_XuCPDIoLNGmy4Dzd92SxlNWCQ707A8tvqP7jQq2wyRBV0M3y0moSs8I03rIXeYNKE3AkMmI8Xp4M6GZd0seJqGvGrN7vA8lJbiBfmEgtcSvPZF0hrfkVRvQGq9uHRx9JOLtdkujsYHk6TW7rYBVsQ"
# page 1504
b = "6SQk6G2z:GBK-59tY9cXfYPiYfpgB1rj16jFZNwQuke.NUV5ZljqD6daOH4pxgaFcRE7bERjrvfoY4OTl5PAWUo70VNRIqnYOi_TQCSWzvrcCgfTtEFl_ZdMHRVLhosJLSFwHiPdVn4cXZ7VnF5xahstqJHD6EBfd71iZT8HQBmx1dssd7RWA2Gdv8lGhJbS0ZeaxIVkfK5qaO.lxHVvG_9cq4weBdHeUQlGlIWhxKFYePkTr9Jp0eN2yDTZljeX0XWWOxIjEkdj89FOqaNDB2slUE.54oC96baGe7lttoz_2AoTbjHSTjfDh.eSyT6vA6.5dP5X.4XsFVYSnYKIznIdkjTURmm3kjvGM_iQoYT3V5gAKs1c6r6cE"
s = difflib.SequenceMatcher(None, a, b, autojunk=False)
for tag, i1, i2, j1, j2 in s.get_opcodes():
if tag != 'equal':
print('{:7} a[{}:{}] --> b[{}:{}] {!r:>8} --> {!r}'.format(
tag, i1, i2, j1, j2, a[i1:i2], b[j1:j2]))
# output
insert a[14:14] --> b[14:49] '' --> '9tY9cXfYPiYfpgB1rj16jFZNwQuke.NUV5Z'
replace a[15:18] --> b[50:55] 'kb7' --> 'jqD6d'
replace a[19:24] --> b[56:60] 'cLMDD' --> 'OH4p'
delete a[25:49] --> b[61:61] 'ywZsCHoJagJlT50f1gw4.jaV' --> ''
insert a[51:51] --> b[63:158] '' --> 'FcRE7bERjrvfoY4OTl5PAWUo70VNRIqnYOi_TQCSWzvrcCgfTtEFl_ZdMHRVLhosJLSFwHiPdVn4cXZ7VnF5xahstqJHD6E'
insert a[52:52] --> b[159:243] '' --> 'fd71iZT8HQBmx1dssd7RWA2Gdv8lGhJbS0ZeaxIVkfK5qaO.lxHVvG_9cq4weBdHeUQlGlIWhxKFYePkTr9J'
insert a[53:53] --> b[244:276] '' --> '0eN2yDTZljeX0XWWOxIjEkdj89FOqaND'
replace a[54:55] --> b[277:291] 'c' --> '2slUE.54oC96ba'
replace a[56:57] --> b[292:311] 'Z' --> 'e7lttoz_2AoTbjHSTjf'
insert a[58:58] --> b[312:369] '' --> 'h.eSyT6vA6.5dP5X.4XsFVYSnYKIznIdkjTURmm3kjvGM_iQoYT3V5gAK'
delete a[60:63] --> b[371:371] 'T_p' --> ''
delete a[64:74] --> b[372:372] 'R_OPFgvOm_' --> ''
replace a[75:84] --> b[373:374] 'oM8PfyL3L' --> 'r'
replace a[85:99] --> b[375:376] 'xDPxFqgIqgwbVA' --> 'c'
delete a[100:377] --> b[377:377] 'w8y4jd0P5yTWo3dx1cNLnCOYTa4mVr7azAXa9YiDEhOz7M1Qsw6BJIOSq0QVp.Ng.NWri7ByAK6dwme99ZEOnjraxZex1xLVGakyVVCoOEhFGfphV8D1GDFKLt1dG.4_XuCPDIoLNGmy4Dzd92SxlNWCQ707A8tvqP7jQq2wyRBV0M3y0moSs8I03rIXeYNKE3AkMmI8Xp4M6GZd0seJqGvGrN7vA8lJbiBfmEgtcSvPZF0hrfkVRvQGq9uHRx9JOLtdkujsYHk6TW7rYBVsQ' --> ''
该GBK
值是通过此调用在页面的 HTML 中创建的。
javascript:commitForECMA(callbackC,"content.jsp?tableId=27&tableName=TABLE27&tableView=杩涘彛鍖荤枟鍣ㄦ浜у搧锛堟敞鍐?&Id=60456",null)
这是被调用的Javascript。
function commitForECMA($_17, $_12, $_19) {
request = createXMLHttp();
request.onreadystatechange = $_17;
if ($_19 == null) {
_$b6(request, _$JI('ZM6r2MG'), _$JI("Op0YV"), $_12);
request.setRequestHeader(_$JI("RACeXwDYXwcTV8Ur2"), _$JI("9wDYgwceLwDT7iCYX3Ce9FKyvHKwPFa"));
} else {
var $_16 = "";
var $_11 = $_19.elements;
var $_14 = $_11.length;
for (var $_4 = 0; $_4 < $_14; $_4++) {
var $_6 = _$kH($_11, $_4);
if ($_6.type != _$JI("aQ6YPMK20") && _$kH($_6, _$JI('Cwbm7wKV')) != "") {
if ($_16.length > 0) {
$_16 += "&" + $_6.name + "=" + _$kH($_6, _$JI('swbm7wKV'));
} else {
$_16 += $_6.name + "=" + _$kH($_6, _$JI('8wbm7wKV'));
}
$_16 += _$JI("xx2J03Up2Hsl");
}
}
_$b6(request, _$JI('iM6r2MG'), _$JI("IVlesYq"), $_12);
$_16 = encodeURI($_16);
$_16 = encodeURI($_16);
request.setRequestHeader(_$JI("53CmOFDVz3CeXwoxBMq"), _$JI("wMbZz3CmOFDV"));
request.setRequestHeader(_$JI("ZACeXwDYXwcTV8Ur2"), _$JI("F3UraMD2O3UpNMCgB8cT6w6QzRbenM1TTQbS2MbJBRDY9"));
}
request.send($_16);
if ($_19 != null) {
$_19.reset();
}
}
truncated....
function createXMLHttp() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
var $_17 = [_$JI("5sYJ3sVanh2fJslf0woqXJ1ga"), _$JI("osYJ3sVanh2fJslf0woqXJcga"), _$JI("ZsYJ3sVanh2fJslf0woqXWnga"), _$JI("fsYJ3sVanh2fJslf0woq"), _$JI("3sK2OQbeuMCR0h2fJslf0woq")];
for (var $_16 = 0; $_16 < $_17.length; $_16++) {
try {
return new ActiveXObject(_$kH($_17, $_16));
} catch ($_19) {}
}
throw new Error("您的浏览器不支持访问此网页");
}
}
truncated....
function callback() {
if (request.readyState == 1) {
_$_J(document.getElementById(_$JI("x3CeXwDYXwq")), '=', _$JI('3FKyXRUxEYlTW'), _$JI("EHDxnHOaB3vE5HDxnHOSNMKQGQ6xOHK2z3Kw2Qne7MCm9FKyvhbwNROg"));
}
if (request.readyState == 4) {
if (request.status == 200) {
oldContent.length = 0;
oldContent[0] = request.responseText;
_$_J(document.getElementById(_$JI("H3CeXwDYXwq")), '=', _$JI('OFKyXRUxEYlTW'), request.responseText);
request = null;
} else {
_$_J(document.getElementById(_$JI("w3CeXwDYXwq")), '=', _$JI('eFKyXRUxEYlTW'), "<br><br><br><span style=font-size:x-large;color:#215add>请勿频繁操作!</span>");
}
}
}
我正在寻找是否有可能获取该值并以某种方式GBK
通过 Python 将其传回。Requests
例如,此代码给我的状态代码为 202。
import requests
headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:89.0) Gecko/20100101 Firefox/89.0",
}
conn_timeout = 30
read_timeout = 60
timeouts = (conn_timeout, read_timeout)
url = 'http://app1.nmpa.gov.cn/data_nmpa/face3/base.jsp?tableId=27&tableName=TABLE27&title=%E8%BF%9B%E5%8F%A3%E5%8C%BB%E7%96%97%E5%99%A8%E6%A2%B0%E4%BA%A7%E5%93%81%EF%BC%88%E6%B3%A8%E5%86%8C&bcId=152904442584853439006654836900'
response = requests.get(url, headers=headers, timeout=timeouts)
print(response.status_code)
# output
202
print(response.cookies)
# output
<RequestsCookieJar[<Cookie acw_tc=3ccdc15616274084596245338e08543386ba17e301ed12362cb2860b0af57f for app1.nmpa.gov.cn/>, <Cookie neCYtZEjo8GmS=5oWYI0i1mRB70b.XyuRJwTdiW_WgqfsIoOM8LNI8nfdTGyX4kfKTl0TDpV5HSMj2KIpgl8ircG4c9uAz_u50UkG for app1.nmpa.gov.cn/>]>
for key, value in response.headers.items():
print(f'Key: {key} -- Value: {value}')
# output
Key: Date -- Value: Tue, 24 Jul 2021 17:54:19 GMT
Key: Content-Type -- Value: text/html; charset=utf-8
Key: Transfer-Encoding -- Value: chunked
Key: Connection -- Value: keep-alive
Key: Set-Cookie -- Value: acw_tc=3ccdc15616274084596245338e08543386ba17e301ed12362cb2860b0af57f;path=/;HttpOnly;Max-Age=1800, neCYtZEjo8GmS=5oWYI0i1mRB70b.XyuRJwTdiW_WgqfsIoOM8LNI8nfdTGyX4kfKTl0TDpV5HSMj2KIpgl8ircG4c9uAz_u50UkG; Path=/; expires=Fri, 25 Jul 2031 17:50:29 GMT; HttpOnly
Key: Server -- Value: ******
Key: Pragma -- Value: no-cache
Key: Cache-Control -- Value: no-store
Key: Expires -- Value: Tue, 24 Jul 2021 17:50:29 GMT
尝试使用 Python 访问网站时收到状态代码 202 是一个问题Requests
,因为在连接关闭之前请求没有被完全处理。
202 接受
请求已被接受处理,但处理尚未完成。该请求最终可能会或可能不会被执行,因为在实际进行处理时它可能会被禁止。无法从诸如此类的异步操作中重新发送状态代码。
202 响应是故意不置可否的。它的目的是允许服务器接受对其他进程的请求(可能是一个每天只运行一次的面向批处理的进程),而不需要用户代理与服务器的连接持续到进程完成。与此响应一起返回的实体应该包括请求当前状态的指示以及指向状态监视器的指针或用户可以期望何时完成请求的一些估计。
我在浏览器中注意到我得到了这些项目。
我仍在探索如何在 Python Requests
POST 中传递这些项目以获取有用的信息。
我没有看过使用selenium,
,但该包似乎是从您的问题中从该网站提取动态创建的内容的最佳选择。
推荐阅读
- python - Django/Python 缓存解决方案,我可以使用复杂的键而不是字符串来访问
- python - 变量和损失评估的奇怪排序
- amazon-web-services - 允许使用小写字母数字字符和连字符的 Terraform RDS 实例错误
- python - 从熊猫数据框中删除方括号
- php - 删除项目时的Laravel分页问题
- kubernetes-helm - helm values.yaml - 使用来自另一个节点的值
- javascript - 在javascript中加载选定的本地文件后如何将行存储到数组中
- r - R中向量列表的平均元素
- javascript - 存储更改时如何记忆组件?
- python - Kivy:无法更改嵌套 GridLayout 的高度