{"id":27029,"date":"2026-11-01T19:47:12","date_gmt":"2026-11-01T19:47:12","guid":{"rendered":"https:\/\/capskip.com\/?p=27029"},"modified":"2026-11-03T03:35:53","modified_gmt":"2026-11-03T03:35:53","slug":"captcha-or-block","status":"publish","type":"post","link":"https:\/\/capskip.com\/zh\/captcha-or-block\/","title":{"rendered":"\u5982\u4f55\u5728 Python \u722c\u866b\u4e2d\u533a\u5206\u9a8c\u8bc1\u7801\u4e0e\u5c01\u9501"},"content":{"rendered":"<p>When a Python scraper gets a bad response, the first question is CAPTCHA or block, because the answer decides what to do next. A CAPTCHA can be solved. A rate limit needs a wait. A block page means your address or fingerprint is refused, and no token will change that. This guide builds a classifier of about 40 lines that reads the status, the headers and the markup and returns one of six labels, then a fetch loop that calls CapSkip only for the label where a solve can help. CapSkip runs on your own Windows PC with no per-solve fee, so skipping a solve that cannot work saves time, not money.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">What you need<\/h2>\n<ul>\n<li>CapSkip running on a Windows PC, with the reCAPTCHA solver switched on. The <a href=\"https:\/\/capskip.com\/setup-guide\/\">setup guide<\/a> covers the first start.<\/li>\n<li>Python 3.10 or later. The code below ran on Python 3.14.0 with requests 2.33.0 and capskip 1.3.0.<\/li>\n<li>A PowerShell prompt. The commands use PowerShell syntax.<\/li>\n<li>Pages you own or may automate.<\/li>\n<li>An address for the solver. In Local mode CapSkip answers on 127.0.0.1 for that device only. In Server mode it listens on your network address or public IP, so a scraper on another PC, a VPS or a hosted platform can reach it over the API. Both are set under <a href=\"https:\/\/capskip.com\/setup-guide\/#connection-settings\">connection settings<\/a>.<\/li>\n<\/ul>\n<p>How this was tested: the classifier and the fetch loop passed 17 checks on Python 3.14.0, each one a response served by a local fixture site. The solves went through the real capskip package to a mock CapSkip, not the app. Two public pages behind Cloudflare&#8217;s full-page check were also fetched on 29 October 2026, and both returned a 403 with a cf-mitigated header. No number below is a solve time.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Why CAPTCHA or block is the first question<\/h2>\n<p>The two mistakes cost different things. Sending a block page to a solver wastes the solve and leaves you blocked. Rotating to a new proxy every time a CAPTCHA appears burns addresses you did not need to burn. A page that serves a CAPTCHA is usually asking you to prove something, and a solved token lets you through. A page that serves a block is refusing you outright. The <a href=\"https:\/\/capskip.com\/http-429-too-many-requests\/\">HTTP 429 guide<\/a> covers the waiting side of this for one status code. This guide covers the decision that comes before it.<\/p>\n<p>Six labels cover it: ok, rate_limited, captcha, cloudflare_challenge, blocked and unknown. The unknown label matters. A classifier forced to choose among the other five will sometimes choose wrong, and a loop that raises on unknown shows you the case you have not handled yet.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 1: install<\/h2>\n<p>Create a virtual environment in an empty folder and install the two packages. The commands call the environment&#8217;s own Python by path, so you do not need to activate it.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"bash\" class=\"EnlighterJSRAW\">py -m venv .venv\n.\\.venv\\Scripts\\python.exe -m pip install capskip requests<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 2: the signals<\/h2>\n<p>Save this block and the next one as classify.py. The first block holds the patterns. The widget pattern looks for a tag whose class attribute names a widget, or an altcha-widget tag. A blog post that shows the markup inside escaped code, a link whose address contains a widget name, and a hidden g-recaptcha-response input all fail to match.<\/p>\n<p>The last line holds phrases that identify a soft block, a page that returns a normal status but says you are refused. Be careful with it. Cloudflare&#8217;s error documentation lists Error 1020: Access denied, which is where the first two phrases come from. The other two are guesses from block pages in the wild, and your target will have its own wording. Replace them after you see a few real ones.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import re\n\n# A tag whose class attribute names a widget, or an altcha-widget tag. A link, a hidden\n# input or a sentence that merely contains the name does not match.\nCLS = r&quot;&lt;[^&gt;]*\\bclass\\s*=\\s*[\\&quot;'][^\\&quot;']*(?&lt;![\\w-])%s(?![\\w-])&quot;\nWIDGET = re.compile(CLS % &quot;(?:g-recaptcha|h-captcha|cf-turnstile|frc-captcha|captchafox)&quot;\n                    + r&quot;|&lt;altcha-widget\\b&quot;, re.I)\nTITLE = re.compile(r&quot;&lt;title[^&gt;]*&gt;(.*?)&lt;\/title&gt;&quot;, re.I | re.S)\nBLOCK = (&quot;access denied&quot;, &quot;error 1020&quot;, &quot;unusual traffic&quot;, &quot;you have been blocked&quot;)<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 3: decide CAPTCHA or block<\/h2>\n<p>The order of the checks is the logic. Each rule only runs if the ones above it did not match.<\/p>\n<ol>\n<li>The cf-mitigated header comes first. Cloudflare documents the value challenge as the one it sends on challenge pages, and it is the strongest signal in the function.<\/li>\n<li>A redirect is checked next, for callers that turn off redirect following. It is called unknown, because the address alone cannot say whether it leads to a CAPTCHA or to an article with the word in its name.<\/li>\n<li>The _cf_chl_opt script variable and the Just a moment title come after that. They catch a challenge page when a proxy or a CDN stripped the header.<\/li>\n<li>The expect argument lets you name a string that only the real page contains. When it is present the page is called ok, whatever the status. Without it, a content page with a CAPTCHA widget in its footer would be called a CAPTCHA.<\/li>\n<li>A widget in the markup means captcha, even on a 429 or a 403. This runs before the status rules, because some sites serve their CAPTCHA with an error status. The widget list covers reCAPTCHA, hCaptcha, Turnstile, Friendly Captcha, CaptchaFox and ALTCHA.<\/li>\n<li>A 429, or a 503 with Retry-After, means rate_limited. A 403 with an HTML body means blocked, unless the page text contains csrf. A JSON 403 is left as unknown, because an API that says your token lacks a scope is not telling you to rotate.<\/li>\n<li>A response with a status below 400 is a soft block when its title contains one of the phrases, or when the whole page is shorter than 3,000 characters and contains one.<\/li>\n<\/ol>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def classify(resp, expect=None):\n    &quot;&quot;&quot;expect: a string only the real page contains, for pages that carry a widget anyway.&quot;&quot;&quot;\n    code, h = resp.status_code, resp.headers\n    if h.get(&quot;cf-mitigated&quot;, &quot;&quot;).lower() == &quot;challenge&quot;:\n        return &quot;cloudflare_challenge&quot;                 # Cloudflare docs\n    if 300 &lt;= code &lt; 400:                             # allow_redirects=False callers\n        return &quot;unknown&quot;\n    html = &quot;html&quot; in h.get(&quot;content-type&quot;, &quot;&quot;).lower()\n    text = resp.text[:200000] if html else &quot;&quot;\n    low = text.lower()\n    if &quot;_cf_chl_opt&quot; in text or &quot;&lt;title&gt;just a moment&quot; in low:\n        return &quot;cloudflare_challenge&quot;                 # header stripped by a proxy\n    if expect and expect in text:\n        return &quot;ok&quot;\n    if WIDGET.search(text):\n        return &quot;captcha&quot;                              # even on a 429 or a 403\n    if code == 429 or (code == 503 and &quot;retry-after&quot; in h):\n        return &quot;rate_limited&quot;\n    if code == 403 and html and &quot;csrf&quot; not in low:\n        return &quot;blocked&quot;                              # JSON 403s fall through\n    m = TITLE.search(text)\n    head = m.group(1).lower() if m else &quot;&quot;\n    small = len(text) &lt; 3000                          # block pages are short\n    if code &lt; 400 and html and any(b in head or (small and b in low) for b in BLOCK):\n        return &quot;blocked&quot;                              # soft block: 200 with a notice\n    return &quot;ok&quot; if code &lt; 400 else &quot;unknown&quot;<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 4: read Retry-After<\/h2>\n<p>Save this block, the next one and the one after that as policy.py. A 429 may carry a Retry-After header, and the header may hold seconds or an HTTP date. MDN lists both forms, and it says the header is optional on a 429. The helper handles both and falls back to a number you choose when the header is missing or unreadable.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import re\nimport time\nfrom datetime import datetime, timezone\nfrom email.utils import parsedate_to_datetime\nfrom urllib.parse import urljoin\n\nfrom classify import classify\n\n\ndef retry_delay(resp, fallback):\n    raw = resp.headers.get(&quot;Retry-After&quot;)\n    if not raw:\n        return fallback\n    try:\n        return max(1.0, float(raw))                        # delay-seconds\n    except ValueError:\n        pass\n    try:\n        when = parsedate_to_datetime(raw)                  # HTTP-date\n        return max(1.0, (when - datetime.now(timezone.utc)).total_seconds())\n    except (TypeError, ValueError):\n        return fallback<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 5: solve the CAPTCHA<\/h2>\n<p>This is the only place CapSkip is called. It reads the sitekey and the form action from the page, asks CapSkip for a token and posts it back in the g-recaptcha-response field. It handles reCAPTCHA v2 only, on purpose, and it posts that one field and nothing else, so a form with other required fields needs them added to the data. Every widget in the list gets the captcha label, so a Turnstile, hCaptcha, Friendly Captcha, CaptchaFox or ALTCHA page reaches this function too. It stops those with a RuntimeError that says to run the <a href=\"https:\/\/capskip.com\/detect-captcha-type\/\">CAPTCHA type detector<\/a> first. Sending a Turnstile key to the reCAPTCHA method instead would end in ERROR_GOOGLEKEY: CapSkip refuses a key that does not look like a reCAPTCHA key, which I confirmed on a mock that copies the app&#8217;s key check, not on the app. For the reCAPTCHA call itself, see the <a href=\"https:\/\/capskip.com\/recaptcha-v2-solver\/\">reCAPTCHA v2 solver page<\/a>.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def solve_recaptcha_form(session, resp, solver):\n    &quot;&quot;&quot;reCAPTCHA v2 only. For other types, run the detector first.&quot;&quot;&quot;\n    key = re.search(r&quot;data-sitekey=[\\&quot;']([^\\&quot;']+)&quot;, resp.text)\n    form = re.search(r&quot;&lt;form[^&gt;]*action=[\\&quot;']([^\\&quot;']*)&quot;, resp.text)\n    if &quot;g-recaptcha&quot; not in resp.text or not (key and form):\n        raise RuntimeError(&quot;not a plain reCAPTCHA v2 form: run the detector first&quot;)\n    token = solver.recaptcha(sitekey=key.group(1), url=resp.url)[&quot;code&quot;]\n    return session.post(urljoin(resp.url, form.group(1)),\n                        data={&quot;g-recaptcha-response&quot;: token}, timeout=30)<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Step 6: the fetch loop<\/h2>\n<p>The loop acts on the CAPTCHA or block decision. It turns each label into one action and tries up to four times. A rate limit sleeps for the header value, with a floor of 1 second and a cap of 60. The cap means a Retry-After of a day is retried after a minute, which can extend the limit. A bare 429, one with no Retry-After, gets two waits of 5 seconds and then a rotation, and the count starts again for the new address. With four tries that is two waits, a rotation and one more wait. A 429 that carries Retry-After never rotates. A block calls your rotate function and never reaches the solver. Your rotate function should change what the next request looks like, for example by setting session.proxies, or raise to stop the run. A challenge page calls your on_challenge function, which should leave a valid cf_clearance cookie on session.cookies, because the values the Turnstile method needs for a full-page challenge are not in the HTML a plain request receives. After either function returns, the loop simply requests the URL again. On success the function returns the response to the form post, so if your target page is a different one, request it again. Each CAPTCHA try costs one solve and can wait up to five minutes, so four failing tries can use four solves. A NetworkException means CapSkip is not reachable and is not caught here. The <a href=\"https:\/\/capskip.com\/cf-clearance-cookie\/\">cf_clearance guide<\/a> covers the cookie that comes after.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">def fetch(session, url, solver, rotate, on_challenge,\n          tries=4, sleep=time.sleep, expect=None):\n    limited = 0\n    for _ in range(tries):\n        resp = session.get(url, timeout=30)\n        kind = classify(resp, expect)\n        if kind == &quot;ok&quot;:\n            return resp\n        if kind == &quot;rate_limited&quot;:\n            limited += 1\n            if limited &gt; 2 and &quot;Retry-After&quot; not in resp.headers:\n                rotate(session)                    # a bare 429 that outlasts two waits\n                limited = 0                        # the new address starts fresh\n            else:\n                sleep(min(retry_delay(resp, 5.0), 60.0))\n        elif kind == &quot;captcha&quot;:\n            resp = solve_recaptcha_form(session, resp, solver)\n            if classify(resp, expect) == &quot;ok&quot;:\n                return resp\n        elif kind == &quot;cloudflare_challenge&quot;:\n            on_challenge(session, resp)            # yours: a browser, or the Turnstile guide\n        elif kind == &quot;blocked&quot;:\n            rotate(session)                        # never solve a block\n        else:\n            raise RuntimeError(f&quot;unknown response {resp.status_code} from {url}&quot;)\n    raise RuntimeError(f&quot;gave up on {url} after {tries} tries&quot;)<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Run it<\/h2>\n<p>Save this as use.py next to the other two files. Replace the address, and put your own proxy logic in rotate. The sample rotate raises, so a blocked run stops instead of asking the same address four times. Run it with the environment&#8217;s own Python, as in .\\.venv\\Scripts\\python.exe use.py. The port 8080 is the default; use the port shown in CapSkip&#8217;s connection settings if yours differs. With the placeholder address the script ends with an unknown response error, which is the expected result.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import requests\nfrom capskip import CapSkip\nfrom policy import fetch\n\nsolver = CapSkip(host=&quot;127.0.0.1&quot;, port=8080)\n\n\ndef rotate(session):\n    raise RuntimeError(&quot;blocked: change the proxy here, or stop the run&quot;)\n\n\ndef on_challenge(session, resp):\n    raise RuntimeError(&quot;Cloudflare challenge page: needs a browser&quot;)\n\n\nsession = requests.Session()\npage = fetch(session, &quot;https:\/\/example.com\/data&quot;, solver, rotate, on_challenge)\nprint(page.status_code, len(page.text))<\/pre>\n<\/div>\n<p>To see what the classifier makes of real responses before you trust it, run this check on a few URLs of your own. In a run on 29 October 2026 it printed ok for a plain page, rate_limited for a 429 and blocked for a 403 from a test service.<\/p>\n<div data-no-translation>\n<pre data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">import requests\nfrom classify import classify\n\nURLS = [\n    &quot;https:\/\/example.com\/&quot;,\n    &quot;https:\/\/httpbin.org\/status\/429&quot;,\n    &quot;https:\/\/httpbin.org\/status\/403&quot;,\n]\n\nfor url in URLS:\n    resp = requests.get(url, timeout=30)\n    print(classify(resp), resp.status_code, url)<\/pre>\n<\/div>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Where the classifier is wrong<\/h2>\n<p>It is a set of heuristics, and these cases are known.<\/p>\n<table>\n<thead>\n<tr>\n<th>Case<\/th>\n<th>Result<\/th>\n<th>Fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Content page with a CAPTCHA widget in the footer<\/td>\n<td>captcha<\/td>\n<td>Pass expect to classify, or to fetch, with a string only the real page holds<\/td>\n<\/tr>\n<tr>\n<td>Blog post with unusual traffic in its title<\/td>\n<td>blocked<\/td>\n<td>Replace the phrases in BLOCK with your target&#8217;s own<\/td>\n<\/tr>\n<tr>\n<td>HTML 403 from the app itself, such as a login page<\/td>\n<td>blocked<\/td>\n<td>Add a rule for that page before the 403 rule<\/td>\n<\/tr>\n<tr>\n<td>Direct visit, or a redirect, to a \/captcha page with an image only<\/td>\n<td>ok<\/td>\n<td>Add a rule for that site<\/td>\n<\/tr>\n<tr>\n<td>Button with the g-recaptcha class and a data-action attribute (reCAPTCHA v3 binding)<\/td>\n<td>captcha<\/td>\n<td>That widget never gates the page: pass expect, or run the detector<\/td>\n<\/tr>\n<tr>\n<td>CAPTCHA added by JavaScript after load<\/td>\n<td>ok<\/td>\n<td>The classifier sees only the first response<\/td>\n<\/tr>\n<tr>\n<td>Site-wide reCAPTCHA v3 script<\/td>\n<td>ok<\/td>\n<td>Intended: v3 never gates the page<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Common errors<\/h2>\n<table>\n<thead>\n<tr>\n<th>Error<\/th>\n<th>Cause<\/th>\n<th>Fix<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>RuntimeError: unknown response<\/td>\n<td>A status the rules do not cover, such as a JSON 403 or a 404<\/td>\n<td>Print the body and add a rule, or let it stop the run<\/td>\n<\/tr>\n<tr>\n<td>RuntimeError: gave up<\/td>\n<td>Four tries without an ok<\/td>\n<td>Check which label repeated, and raise tries only if it was rate_limited<\/td>\n<\/tr>\n<tr>\n<td>RuntimeError: not a plain reCAPTCHA v2 form<\/td>\n<td>The captcha label came from another widget, or the page has no sitekey or no form<\/td>\n<td>Run the detector and call the matching method<\/td>\n<\/tr>\n<tr>\n<td>ERROR_GOOGLEKEY<\/td>\n<td>The key does not look like a reCAPTCHA key, for example a Turnstile key or the word explicit<\/td>\n<td>Read the key from the widget itself, or run the detector<\/td>\n<\/tr>\n<tr>\n<td>ApiException or TimeoutException<\/td>\n<td>The solve failed or ran past the SDK&#8217;s limit<\/td>\n<td>Check the label that preceded it, and keep tries low so a failing CAPTCHA does not use many solves<\/td>\n<\/tr>\n<tr>\n<td>NetworkException<\/td>\n<td>CapSkip is not running, or the host or port is wrong<\/td>\n<td>Start CapSkip and check the connection settings, see the <a href=\"https:\/\/capskip.com\/api-docs\/\">API docs<\/a><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">Running the solver on a server<\/h2>\n<p>The loop needs only a host and a port for CapSkip. In Local mode that is 127.0.0.1 on the same PC. In Server mode CapSkip listens on your network address or a public IP, so a scraper on another PC, a VPS or a hosted platform calls the same API. Change the host and port in the CapSkip constructor and nothing else changes. A static public IP is recommended for stable remote access. Server mode is still your own hardware and still unmetered. It opens the port to any address, so add a Windows Firewall block rule for the addresses that should not reach it. The <a href=\"https:\/\/capskip.com\/setup-guide\/#connection-settings\">connection settings<\/a> explain how to switch modes.<\/p>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">FAQ<\/h2>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">How do I know if I am blocked or just rate limited?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">A 429, especially with a Retry-After header, is a rate limit, and waiting works. A 403 with an HTML body, or a 200 page that says access is denied, is a block. A bare 429 that outlasts two waits is treated as a block, as a rule of thumb, which is why the loop rotates on it.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Can a solved CAPTCHA fix a block?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">No. A block refuses your address or your fingerprint, and a token proves nothing about either. The loop skips the solver on a block and rotates instead.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Why does the loop not solve the Cloudflare challenge page?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">The Turnstile method needs two values from the challenge, and a plain request does not receive them. The loop hands that case to a function you supply, such as one that drives a real browser, and keeps the classifier honest about what it can see.<\/p>\n<\/details>\n<details style=\"border:1px solid #e2e5ee;border-radius:10px;padding:14px 18px;margin:0 0 12px;\">\n<summary style=\"cursor:pointer;\">\n<h3 style=\"font-size:1.15rem;line-height:1.4;display:inline;margin:0;\">Can this run against CapSkip on another machine?<\/h3>\n<\/summary>\n<p style=\"margin:12px 0 0;\">Yes. Use Server mode, point the CapSkip constructor at that host and port, and the loop works as it does on one PC. The solver stays on your hardware.<\/p>\n<\/details>\n<h2 style=\"font-size:1.6rem;line-height:1.35;\">The short version<\/h2>\n<p>Decide CAPTCHA or block first, act second: solve the CAPTCHA, wait out the rate limit, rotate away from the block and stop on anything unknown. With CapSkip on your own machine, the saving from skipping a doomed solve is time, and the saving from not rotating needlessly is addresses. For a broader view of getting past CAPTCHA pages, the <a href=\"https:\/\/capskip.com\/\">captcha bypass<\/a> approach is to solve what can be solved and route around the rest. For the Python SDK itself, see the <a href=\"https:\/\/capskip.com\/python-captcha-solver\/\">Python CAPTCHA solver page<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u5bf9\u6bcf\u4e2a\u88ab\u62e6\u622a\u7684\u9875\u9762\u90fd\u53bb\u8bc6\u522b\uff0c\u53ea\u4f1a\u6d6a\u8d39\u8bc6\u522b\u6b21\u6570\uff1b\u6bcf\u9047\u5230\u9a8c\u8bc1\u7801\u5c31\u8f6e\u6362\u4ee3\u7406\uff0c\u53c8\u4f1a\u767d\u767d\u70e7\u6389 IP\u3002\u4e0b\u9762\u662f\u4e00\u4e2a Python \u5206\u7c7b\u5668\uff1a\u5b83\u8bfb\u53d6\u72b6\u6001\u7801\u3001\u54cd\u5e94\u5934\u548c\u9875\u9762\u6807\u8bb0\uff0c\u518d\u544a\u8bc9 CapSkip \u4f55\u65f6\u8be5\u8bc6\u522b\u3001\u4f55\u65f6\u8be5\u9000\u907f\u3002<\/p>","protected":false},"author":1,"featured_media":27028,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"CAPTCHA or Block? Classify Python Responses | CapSkip","rank_math_description":"CAPTCHA or block? Classify each Python response as ok, rate limited, CAPTCHA, challenge page or block, and call CapSkip only when a solve can help.","rank_math_focus_keyword":"captcha or block","footnotes":""},"categories":[70],"tags":[],"class_list":["post-27029","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-captcha"],"_links":{"self":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/27029","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/comments?post=27029"}],"version-history":[{"count":3,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/27029\/revisions"}],"predecessor-version":[{"id":27226,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/posts\/27029\/revisions\/27226"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media\/27028"}],"wp:attachment":[{"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/media?parent=27029"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/categories?post=27029"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/capskip.com\/zh\/wp-json\/wp\/v2\/tags?post=27029"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}