[英]Request headers to scrape pypi.org
我正在嘗試使用請求庫和漂亮的湯來截屏 PyPI 包 - 但遇到了無限期的掛起。 我能夠從許多站點檢索 html:
session = requests.Session()
session.trust_env = False
response = session.get("http://google.com")
print(response.status_code)
即不提供標題。 我從Python 中讀到 request.get 無法得到 url 的答案我可以在我的瀏覽器上打開無限期掛起可能是由不正確的標頭引起的。 因此,使用開發人員工具,我嘗試使用“Doc”過濾器從“網絡”選項卡(使用 Edge)獲取我的請求標頭到 select pypi.org
響應/請求。 我只是將這些復制粘貼到傳遞給get
方法的 header 變量中:
headers = {'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9',
'cookie': 'session_id=<long string>',
'dnt': '1',
'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Microsoft Edge";v="108"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'none',
'sec-fetch-user': '?1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54'}
(並將 get 方法更改為response = session.get("http://pypi.org", headers=headers)
)
但我也一樣。 所以,我認為我的標題有問題,但我不確定是什么。 我知道請求Session()
“處理” cookies 所以我嘗試在我的請求 header 字典中刪除cookie
鍵/值對但取得了相同的結果。
我如何確定我的標頭的問題和/或為什么我當前的標頭不起作用(假設這甚至是問題所在)?
我嘗試發送一個簡單的 HTTP 請求來查看此服務器是否需要任何標頭才能正常響應。
因此,我打開了一個 TCP 套接字並連接到 Pypi 服務器,以查看在沒有框架干預的情況下服務器如何處理請求。 此外,我們將該套接字包裝在 SSL 庫中以發送加密流量 (HTTPS)
import socket
import ssl
hostname = 'pypi.org'
context = ssl.create_default_context()
payld = ("GET / HTTP/1.1\r\n"
f"Host: {hostname}\r\n\r\n")
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
text = payld
ssock.sendall(text.encode())
print(ssock.recv(40))
OUTPUT (只是響應的前40個字節,但是我們可以看到狀態碼,是200 OK):
b'HTTP/1.1 200 OK\r\nConnection: keep-alive\r'
結果,我們可以得出headers 沒有效果的結論。
我建議您嘗試此代碼。
HTTP 標頭是一個可能的問題,但可能性不大。 更可能的原因是代理/防火牆。 我將從回顧評論中我認為相關的信息開始;
http://pypi.org
在您的瀏覽器中運行。http://pypi.org
從您系統上的 PowerShell 工作。http://pypi.org
與您的 python 代碼一起掛起。 由於您的瀏覽器和 PowerShell 似乎都工作正常,如果您沒有更改它們的設置,您為什么要嘗試使用 python 繞過代理? (@vader 在評論中提出這個問題,我沒有看到相關回復)
如果繞過代理對您的目標很重要,請將本節跳至下一節(在單杠之后)。 如果不是,因為其他程序似乎工作正常,我建議使用系統的原始配置嘗試使用代理;
session.trust_env = False
語句。如果腳本仍然掛在對 pypi 的請求上,則需要進一步分析。 為了縮小選擇范圍,我建議如下:
allow_redirects=False
禁用重定向。 如果存在重定向循環, requests
應該引發TooManyRedirects
異常,這將有助於識別重定向目標掛起的情況。 pypi 應該將http
重定向到https
而不管用戶代理或大多數其他標頭,這使得請求一致、可靠,從而限制了其他可能的因素。下面的代碼提供了一個很好的例子。 對於您的代碼,不要使用端口號,默認值應該可以工作。 我明確地添加了端口號,因為每個端口號都展示了一種不同的可能情況:
#!/usr/bin/env python
import socket
import timeit
import requests
TIMEOUT = (4, 7) # ConnectT/O (per-IP), ReadT/O
def get_url(url, timeout=TIMEOUT):
try:
response = requests.get(url, timeout=timeout, allow_redirects=False)
print(f"Status code: {response.status_code}", end="")
if response.status_code in (301, 302):
print(f", Location: {response.headers.get('location')}", end="")
print(".")
except Exception as e:
print(f"Exception caught: {e!r}")
finally:
print(f"Fetching url '{url}' done", end="")
def time_url(url):
print(f"Trying url '{url}'")
total = timeit.timeit(f"get_url('{url}')", number=1, globals=globals())
print(f" in: {str(total)[:4]} seconds")
print("=============")
def print_expected_conntimeout(server):
r = socket.getaddrinfo(server, None, socket.AF_UNSPEC, socket.SOCK_STREAM)
print(f"IP addresses of {server}:\n" + "\n".join(addr[-1][0] for addr in r))
print(f"Got {len(r)} addresses, so expecting a a total ConnectTimeout of {len(r) * TIMEOUT[0]}")
def main():
scheme = "http://"
server = "pypi.org"
uri = f"{scheme}{server}:{{port}}".format
print_expected_conntimeout(server)
# OK/redirect (301)
time_url(uri(port=80))
# READ TIMEOUT after 7s
time_url(uri(port=8080))
# CONNECTION TIMEOUT after 4 * ip_addresses
time_url(uri(port=8082))
# REJECT
time_url('http://localhost:80')
if __name__ == "__main__":
main()
對我來說,這輸出:
$ ./testnet.py
IP addresses of pypi.org:
151.101.128.223
151.101.0.223
151.101.64.223
151.101.192.223
Got 4 addresses, so expecting a a total ConnectTimeout of 16
Trying url 'http://pypi.org:80'
Status code: 301, Location: https://pypi.org/.
Fetching url 'http://pypi.org:80' done in: 0.66 seconds
=============
Trying url 'http://pypi.org:8080'
Exception caught: ReadTimeout(ReadTimeoutError("HTTPConnectionPool(host='pypi.org', port=8080): Read timed out. (read timeout=7)"))
Fetching url 'http://pypi.org:8080' done in: 7.21 seconds
=============
Trying url 'http://pypi.org:8082'
Exception caught: ConnectTimeout(MaxRetryError("HTTPConnectionPool(host='pypi.org', port=8082): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x103ec4730>, 'Connection to pypi.org timed out. (connect timeout=4)'))"))
Fetching url 'http://pypi.org:8082' done in: 16.0 seconds
=============
Trying url 'http://localhost:80'
Exception caught: ConnectionError(MaxRetryError("HTTPConnectionPool(host='localhost', port=80): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x103ec44c0>: Failed to establish a new connection: [Errno 61] Connection refused'))"))
Fetching url 'http://localhost:80' done in: 0.00 seconds
=============
現在解釋四種情況:
http://pypi.org
的成功請求返回 301 重定向 - 使用 https。allow_redirects=False
后得到的結果,那么主要嫌疑人就是重定向鏈,我建議類似地檢查每個location
標頭的值以獲取您收到的每個重定向響應,直到找到掛起的 URL。requests
引發ReadTimeout
異常。trust_env
,或者一些附加到網絡基礎設施的設備。requests
引發ConnectTimeout
異常。 請注意,將嘗試連接到找到的每個 IP 地址,因此 4 秒的超時將乘以總體地址數量。另一件值得關注的事情是 pypi 的 IP 地址,因為它們是由提供的腳本打印的。 雖然不能保證 IPv4 地址會保留它們的分配,但在這種情況下,如果您發現它們有很大不同 - 這表明您實際上沒有連接到真正的 pypi 服務器,因此響應是不可預測的(包括掛起)。 以下是 pypi 的 IPv4 和 IPv6 地址:
pypi.org has address 151.101.0.223
pypi.org has address 151.101.64.223
pypi.org has address 151.101.128.223
pypi.org has address 151.101.192.223
pypi.org has IPv6 address 2a04:4e42::223
pypi.org has IPv6 address 2a04:4e42:200::223
pypi.org has IPv6 address 2a04:4e42:400::223
pypi.org has IPv6 address 2a04:4e42:600::223
最后,由於我們已經觸及了不同的 IP 協議版本,也有可能在啟動連接時,您的系統嘗試使用到目的地的路由錯誤的協議(例如嘗試使用 IPv6,但其中一個網關處理不當)交通)。 通常路由器會回復 ICMP 失敗消息,但我見過這種情況沒有發生(或沒有正確中繼回來)的情況。 由於路由不受我的控制,我無法確定根本原因,但強制執行特定協議為我解決了該特定問題。
希望這提供了一些好的調試向量,如果這有幫助請添加評論,因為我很好奇你找到了什么。
知道了!
我只需要在 get 方法中設置代理變量:
headers={'User-Agent': 'Chrome'}
proxies = {
'http': 'xxxxxx:80',
'https': 'xxxxxx:80',
}
def get_url(url):
try:
response = requests.get(url, timeout=10, allow_redirects=True, headers=headers, proxies=proxies)
print(response.headers)
print(response.text)
print(response.history)
print(f"Status code: {response.status_code}")
if response.status_code in (301, 302):
print(f", Location: {response.headers.get('location')}")
except Exception as e:
print(f"Exception caught: {e!r}")
finally:
print(f"Fetching url '{url}' done", end="")
url = "http://pypi.org"
get_url(url)
你確定嗎? 只有 pypi 的主頁會引發您在任何情況下都無法抓取的錯誤,您是否有防火牆或 https 或 socks 代理?
我已經為 python 3 的所有包裹取了 url,這個代碼工作得很好
import requests
from bs4 import BeautifulSoup
hdr={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5"}
session = requests.Session()
url="https://pypi.org/search/?c=Programming+Language+%3A%3A+Python+%3A%3A+3" # All Python 3 libraries
response = session.get(url,allow_redirects=True)
print(response.status_code)
> 200
只是為了確保..讓我們抓取 package 名稱來驗證
soup=BeautifulSoup(response.content,"lxml")
pkgs=soup.findAll('span',attrs={'class':'package-snippet__name'})
for i in pkgs:
print(i.text)
>
yingyu-yueyueyue-201812-201909
github-actions-cicd-example
xurl
unkey
fluvio
LogicCircuit
knarrow
riyu-zhuanye-kaoyan-202203-202206
permutation
aliases
sangsangjun-202011-202101
resultify
subnuker
keke-yingyu-202101-202104
xuezhaofeng-beida-jingjixue
jingtong-jiaoben-heike
mrbenn-toolbar-plugin
liuwei-yasi-pindao-201811-201908
mypy-boto3-service-quotas
trender
第 1 頁所有 python 3 個包的名稱
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.