web-dev-qa-db-fra.com

Les demandes de Python déclenchent la sécurité de Cloudflare tandis que Urllib ne

Je travaille sur un site Web WebScrapper automatisé pour un site Web de restaurant, mais j'ai un problème. Ledit site Web utilise la sécurité anti-bot de CloudLfare, que je voudrais contourner, pas le mode sous-attaque, mais un test CAPTCHA qui déclenche uniquement lorsqu'il détecte une adresse IP non américaine ou un bot. J'essaie de le contourner que la sécurité de Cloudflare ne déclenche pas lorsque je dégage des cookies, désactivez JavaScript ou lorsque j'utilise un proxy américain.

Connaissant cela, j'ai essayé d'utiliser la bibliothèque de demandes de Python en tant que telle:

import requests
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0'}
response = requests.get("https://grimaldis.myguestaccount.com/guest/accountlogin", headers=headers).text
print(response)

Mais cela finit par déclencher Cloudflare, peu importe le proxy que j'utilise.

CEPENDANT Lorsque vous utilisez Urllib.Request avec les mêmes en-têtes que tels:

import urllib.request
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0'}
request = urllib.request.Request("https://grimaldis.myguestaccount.com/guest/accountlogin", headers=headers)
r = urllib.request.urlopen(request).read()
print(r.decode('utf-8'))

Une fois couru avec la même adresse IP américaine, cette fois, il ne déclenche pas la sécurité de Cloudflare, même s'il utilise les mêmes en-têtes et IP utilisés avec la bibliothèque de demandes.

Donc, j'essaie de comprendre ce qui déclenche exactement Clougleflare dans la bibliothèque de demandes qui ne figure pas dans la bibliothèque d'Urllib.

Tandis que la réponse typique serait "utilise simplement Urllib, alors", j'aimerais savoir ce qui est différent exactement avec les demandes et comment je pourrais la corriger, tout de suite à comprendre comment les demandes fonctionnent et Cloudflare détecte des bots, mais aussi que Je peux appliquer n'importe quel correctif que je peux trouver sur d'autres httplibs (notamment asynchrones)

Edition n ° 2: Progrès jusqu'à présent :

Grâce à @tuangeek, nous pouvons désormais contourner le bloc Cloudflare à l'aide de demandes tant que nous nous connectons directement à l'hôte IP plutôt que sur le nom de domaine (pour une raison quelconque, la redirection DNS avec les demandes déclenche Cloudflare, mais Urllib ne le fait pas):

import requests
from collections import OrderedDict
import socket

# grab the address using socket.getaddrinfo
answers = socket.getaddrinfo('grimaldis.myguestaccount.com', 443)
(family, type, proto, canonname, (address, port)) = answers[0]
headers = OrderedDict({
    'Host': "grimaldis.myguestaccount.com",
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0',
})
s = requests.Session()
s.headers = headers
response = s.get(f"https://{address}/guest/accountlogin", verify=False).text

À noter: essayer d'accéder via http (plutôt que https avec la vérification de vérification définie sur false) déclenchera le bloc de Cloudflare

Maintenant, c'est génial, mais malheureusement, mon objectif final de rendre ce travail de manière asynchrone avec le HTTPLIB httpx n'est toujours pas rempli, comme en utilisant le code suivant, le bloc Cloudflare est toujours déclenché même si nous vous connectons directement à l'adresse IP hôte, avec Les en-têtes appropriés et avec vérification définie sur FALSE:

import trio
import httpx
import socket
from collections import OrderedDict
answers = socket.getaddrinfo('grimaldis.myguestaccount.com', 443)
(family, type, proto, canonname, (address, port)) = answers[0]
headers = OrderedDict({
    'Host': "grimaldis.myguestaccount.com",
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0',
})
async def asks_worker():
    async with httpx.AsyncClient(headers=headers, verify=False) as s:
        r = await s.get(f'https://{address}/guest/accountlogin')
        print(r.text)
async def run_task():
    async with trio.open_nursery() as nursery:
        nursery.start_soon(asks_worker)
trio.run(run_task)

Edition n ° 1: Pour plus de détails, voici la demande HTTP brute de Urllib et des demandes

Demandes:

send: b'GET /guest/nologin/account-balance HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: grimaldis.myguestaccount.com\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0\r\nConnection: close\r\n\r\n'
reply: 'HTTP/1.1 403 Forbidden\r\n'
header: Date: Thu, 02 Jul 2020 20:20:06 GMT
header: Content-Type: text/html; charset=UTF-8
header: Transfer-Encoding: chunked
header: Connection: close
header: CF-Chl-Bypass: 1
header: Set-Cookie: __cfduid=df8902e0b19c21b364f3bf33e0b1ce1981593721256; expires=Sat, 01-Aug-20 20:20:06 GMT; path=/; domain=.myguestaccount.com; HttpOnly; SameSite=Lax; Secure
header: Cache-Control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
header: Expires: Thu, 01 Jan 1970 00:00:01 GMT
header: X-Frame-Options: SAMEORIGIN
header: cf-request-id: 03b2c8d09300000ca181928200000001
header: Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
header: Set-Cookie: __cfduid=df8962e1b27c25b364f3bf66e8b1ce1981593723206; expires=Sat, 01-Aug-20 20:20:06 GMT; path=/; domain=.myguestaccount.com; HttpOnly; SameSite=Lax; Secure
header: Vary: Accept-Encoding
header: Server: cloudflare
header: CF-RAY: 5acb25c75c981ca1-EWR

Urllib:

send: b'GET /guest/nologin/account-balance HTTP/1.1\r\nAccept-Encoding: identity\r\nHost: grimaldis.myguestaccount.com\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0\r\nConnection: close\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Date: Thu, 02 Jul 2020 20:20:01 GMT
header: Content-Type: text/html;charset=utf-8
header: Transfer-Encoding: chunked
header: Connection: close
header: Set-Cookie: __cfduid=db9de9687b6c22e6c12b33250a0ded3251292457801; expires=Sat, 01-Aug-20 20:20:01 GMT; path=/; domain=.myguestaccount.com; HttpOnly; SameSite=Lax; Secure
header: Expires: Thu, 2 Jul 2020 20:20:01 GMT
header: Cache-Control: no-cache, private, no-store
header: X-Powered-By: Undertow/1
header: Pragma: no-cache
header: X-Frame-Options: SAMEORIGIN
header: Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com https://www.google-analytics.com/analytics.js https://use.typekit.net connect.facebook.net/ https://googleads.g.doubleclick.net/ app.pendo.io cdn.pendo.io pendo-static-6351154740266000.storage.googleapis.com pendo-io-static.storage.googleapis.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://www.google.com/recaptcha/api.js apis.google.com https://www.googletagmanager.com api.instagram.com https://app-rsrc.getbee.io/plugin/BeePlugin.js https://loader.getbee.io api.instagram.com https://bat.bing.com/bat.js https://www.googleadservices.com/pagead/conversion.js https://connect.facebook.net/en_US/fbevents.js  https://connect.facebook.net/ https://fonts.googleapis.com/ https://ssl.gstatic.com/ https://tagmanager.google.com/;style-src 'unsafe-inline' *;img-src * data:;connect-src 'self' app.pendo.io api.feedback.us.pendo.io; frame-ancestors 'self' app.pendo.io pxsweb.com *.pxsweb.com;frame-src 'self' *.myguestaccount.com https://app.getbee.io/ *;
header: X-Lift-Version: Unknown Lift Version
header: CF-Cache-Status: DYNAMIC
header: cf-request-id: 01b2c5b1fa00002654a25485710000001
header: Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
header: Set-Cookie: __cfduid=db9de811004e591f9a12b66980a5dde331592650101; expires=Sat, 01-Aug-20 20:20:01 GMT; path=/; domain=.myguestaccount.com; HttpOnly; SameSite=Lax; Secure
header: Set-Cookie: __cfduid=db9de811004e591f9a12b66980a5dde331592650101; expires=Sat, 01-Aug-20 20:20:01 GMT; path=/; domain=.myguestaccount.com; HttpOnly; SameSite=Lax; Secure
header: Set-Cookie: __cfduid=db9de811004e591f9a12b66980a5dde331592650101; expires=Sat, 01-Aug-20 20:20:01 GMT; path=/; domain=.myguestaccount.com; HttpOnly; SameSite=Lax; Secure
header: Server: cloudflare
header: CF-RAY: 5acb58a62c5b5144-EWR
10
Tom

Après avoir débogué, et grâce aux réponses de @Tuangeek, nous avons découvert que la question de la bibliothèque de demandes semble provenir d'un problème DNS sur la partie des demandes lorsqu'il s'agissait de Cloudflare, une solution simple à ce problème se connecte directement à L'IP hôte comme tel:

import requests
from collections import OrderedDict
from requests import Session
import socket

# grab the address using socket.getaddrinfo
answers = socket.getaddrinfo('grimaldis.myguestaccount.com', 443)
(family, type, proto, canonname, (address, port)) = answers[0]

s = Session()
headers = OrderedDict({
    'Accept-Encoding': 'gzip, deflate, br',
    'Host': "grimaldis.myguestaccount.com",
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0'
})
s.headers = headers
response = s.get(f"https://{address}/guest/accountlogin", headers=headers, verify=False).text
print(response)

Maintenant, ce correctif n'a pas fonctionné lorsque vous travaillez avec le HTTPLIB httpx, mais j'ai trouvé où le problème découle de.

Le problème provient de la bibliothèque H11 (utilisé par httpx pour gérer les demandes HTTP/1.1), tandis que Urllib corrige automatiquement le cas de la lettre des en-têtes, H11 a adopté une approche différente en descendant chaque en-tête. En théorie, cela ne devrait pas causer de problèmes, car les serveurs devraient gérer des en-têtes de manière insensible à une manière insensible (et dans de nombreux cas, la réalité est que HTTP est Hard ™ et des services tels que Cloudflare ne respectent pas le respect RFC2616 et oblige les en-têtes à être correctement capitalisés.

Les discussions sur la capitalisation sont passées depuis longtemps à H11:

https://github.com/python-hyper/h11/issues/31

Et avoir "récemment" commencés à apparaître sur le repo de httpx également:

https://github.com/encode/httpx/issues/538

https://github.com/encode/httpx/issues/728

Maintenant, la réponse insatisfaisante à la question entre Cloudflare et httpx est que jusqu'à ce que quelque chose soit effectué sur le côté de H11 (ou jusqu'à ce que Cloudflare commence miraculeusement sur le RFC2616), il ne peut pas être changé à la hauteur de la capitalisation HTTPX et du cloudflare.

Utilisez un HTTPlib différent tel que AIOHTTP ou Demandes-contrats à terme, essayez de forger et de corriger la capitalisation en-tête avec H11 vous-même, ou attendez et espérez que la question soit traitée correctement par l'équipe H11.

3
Tom