Theo tài liệu, cả Content-Disposition
và thuộc tính filename
của nó đều không cần thiết. Ngoài ra, tôi đã kiểm tra hàng chục liên kết trên internet và không tìm thấy phản hồi với tiêu đề Content-Disposition
. Vì vậy, trong hầu hết các trường hợp, tôi sẽ không dựa vào nó nhiều và chỉ lấy thông tin này từ URL yêu cầu [lưu ý: Tôi đang lấy nó từ
import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
1 vì có thể chuyển hướng và chúng tôi muốn lấy tên tệp thực]. Tôi đã sử dụng import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
2 bởi vì nó trông mạnh mẽ hơn và xử lý các tên tệp được trích dẫn và chưa được trích dẫn. Cuối cùng, tôi đã đưa ra giải pháp này [hoạt động từ Python 3.8]:from urllib.parse import urlparse
import requests
import werkzeug
def get_filename[url: str]:
try:
with requests.get[url] as req:
if content_disposition := req.headers.get["Content-Disposition"]:
param, options = werkzeug.http.parse_options_header[content_disposition]
if param == 'attachment' and [filename := options.get['filename']]:
return filename
path = urlparse[req.url].path
name = path[path.rfind['/'] + 1:]
return name
except requests.exceptions.RequestException as e:
raise e
Tôi đã viết một số bài kiểm tra bằng cách sử dụng
import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
3 và import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
4:import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
Bài đăng này là về cách tải xuống các tệp hiệu quả/chính xác từ các URL bằng Python. Tôi sẽ sử dụng các yêu cầu thư viện của Chúa cho nó. Tôi sẽ viết về các phương thức để tải xuống chính xác các nhị phân từ các URL và đặt tên tệp của chúng.
I will be using the god-send library
requests for it. I will write about methods to correctly download binaries from URLs and set their filenames.
Hãy bắt đầu với các bước của em bé về cách tải xuống tệp bằng các yêu cầu -
import requests
url = '//google.com/favicon.ico'
r = requests.get[url, allow_redirects=True]
open['google.ico', 'wb'].write[r.content]
Mã trên sẽ tải xuống phương tiện truyền thông tại //google.com/favicon.ico và lưu nó dưới dạng google.ico.
Bây giờ chúng ta hãy lấy một ví dụ khác trong đó URL là //www.youtube.com/watch?v=9BZKP7Q19F0. Bạn nghĩ điều gì sẽ xảy ra nếu mã trên được sử dụng để tải xuống? Nếu bạn nói rằng một trang HTML sẽ được tải xuống, bạn sẽ xuất hiện. Đây là một trong những vấn đề tôi gặp phải trong mô -đun nhập của sự kiện mở trong đó tôi phải tải xuống phương tiện từ các liên kết nhất định. Khi URL được liên kết với trang web thay vì nhị phân, tôi phải không tải xuống tệp đó và chỉ giữ liên kết như vậy. Để giải quyết điều này, những gì tôi đã làm là kiểm tra các tiêu đề của URL. Các tiêu đề thường chứa tham số
import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
5 cho chúng ta biết về loại dữ liệu mà URL đang liên kết. Một cách ngây thơ để làm điều đó sẽ là -What do you think will happen if the above code is used to download it ?
If you said that a HTML page will be downloaded, you are spot on. This was one of the problems I faced in the Import module of Open Event where I had to download media from certain links. When the URL linked to a webpage rather than a binary, I had to not download that file and just keep the link as is.
To solve this, what I did was inspecting the headers of the URL. Headers usually contain a
import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
5 parameter which tells us about the type of data the url is linking to.A naive way to do it will be -
r = requests.get[url, allow_redirects=True]
print r.headers.get['content-type']
Nó hoạt động nhưng không phải là cách tối ưu để làm như vậy vì nó liên quan đến việc tải xuống tệp để kiểm tra tiêu đề. Vì vậy, nếu tập tin lớn, điều này sẽ không làm gì khác ngoài băng thông thải. Tôi đã xem xét tài liệu yêu cầu và tìm ra một cách tốt hơn để làm điều đó. Theo cách đó, chỉ cần tìm nạp các tiêu đề của một URL trước khi thực sự tải xuống nó. Điều này cho phép chúng tôi bỏ qua việc tải xuống các tệp không có nghĩa là được tải xuống.
So if the file is large, this will do nothing but waste bandwidth.
I
looked into the requests documentation and found a better way to do it. That way involved just fetching the headers of a url before actually downloading it.
This allows us to skip downloading files which weren't meant to be downloaded.
import requests
def is_downloadable[url]:
"""
Does the url contain a downloadable resource
"""
h = requests.head[url, allow_redirects=True]
header = h.headers
content_type = header.get['content-type']
if 'text' in content_type.lower[]:
return False
if 'html' in content_type.lower[]:
return False
return True
print is_downloadable['//www.youtube.com/watch?v=9bZkp7q19f0']
# >> False
print is_downloadable['//google.com/favicon.ico']
# >> True
Để hạn chế tải xuống theo kích thước tệp, chúng tôi có thể lấy tệp từ tiêu đề
import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
6 và sau đó so sánh phù hợp.content_length = header.get['content-length', None]
if content_length and content_length > 2e8: # 200 mb approx
return False
Vì vậy, sử dụng chức năng trên, chúng tôi có thể bỏ qua các URL tải xuống không liên kết với phương tiện.
Nhận tên tệp từ URL
Chúng ta có thể phân tích URL để lấy tên tệp. Ví dụ - //aviaryan.in/images/profile.png.
Example - //aviaryan.in/images/profile.png.
Để trích xuất tên tệp từ URL trên, chúng tôi có thể viết một thói quen tìm nạp chuỗi cuối cùng sau khi chao đảo [/].
url = '//aviaryan.in/images/profile.png'
if url.find['/']:
print url.rsplit['/', 1][1]
Điều này sẽ được cung cấp cho tên tệp trong một số trường hợp một cách chính xác. Tuy nhiên, có những lúc thông tin tên tệp không có trong URL. Ví dụ, một cái gì đó như
import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
7. Trong trường hợp đó, tiêu đề Content-Disposition
sẽ chứa thông tin tên tệp. Đây là làm thế nào để lấy nó.Example, something like
import pytest
import requests
import requests_mock
from main import get_filename
TEST_URL = '//pwrk.us/report.pdf'
@pytest.mark.parametrize[
'headers,expected_filename',
[
[
{'Content-Disposition': 'attachment; filename="filename.pdf"'},
"filename.pdf"
],
[
# The string following filename should always be put into quotes;
# but, for compatibility reasons, many browsers try to parse unquoted names that contain spaces.
# //developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
{'Content-Disposition': 'attachment; filename=filename with spaces.pdf'},
"filename with spaces.pdf"
],
[
{'Content-Disposition': 'attachment;'},
"report.pdf"
],
[
{'Content-Disposition': 'inline;'},
"report.pdf"
],
[
{},
"report.pdf"
]
]
]
def test_get_filename[headers, expected_filename]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, text='resp', headers=headers]
assert get_filename[TEST_URL] == expected_filename
def test_get_filename_exception[]:
with requests_mock.Mocker[] as m:
m.get[TEST_URL, exc=requests.exceptions.RequestException]
with pytest.raises[requests.exceptions.RequestException]:
get_filename[TEST_URL]
7. In that case, the Content-Disposition
header will contain the filename information.Here is how to fetch it.
import requests
import re
def get_filename_from_cd[cd]:
"""
Get filename from content-disposition
"""
if not cd:
return None
fname = re.findall['filename=[.+]', cd]
if len[fname] == 0:
return None
return fname[0]
url = '//google.com/favicon.ico'
r = requests.get[url, allow_redirects=True]
filename = get_filename_from_cd[r.headers.get['content-disposition']]
open[filename, 'wb'].write[r.content]
Mã phân tích URL kết hợp với phương thức trên để lấy tên tệp từ tiêu đề Content-Disposition
sẽ hoạt động cho hầu hết các trường hợp. Sử dụng chúng và kiểm tra kết quả.
Use them and test the results.
Đây là 2 xu của tôi khi tải xuống các tệp bằng cách sử dụng các yêu cầu trong Python. Hãy cho tôi biết về các thủ thuật khác mà tôi có thể đã bỏ qua.
Bài viết này lần đầu tiên được đăng trên blog cá nhân của tôi.
Thưởng thức bài viết này? Cung cấp cho Avi Aryan như nếu nó hữu ích.Avi Aryan a like if it's helpful.
Nhà phát triển web Stack Full Stack tại Toptal, GSOC 17 & Udacity Mentor
Tôi là một nhà phát triển tự do hiện đang làm việc tại Toptal và Udacity. Tôi chuyên môn trong phát triển web Stack đầy đủ. Tôi đã lập trình được 6 năm và tôi tin vào sự tỉnh táo của mã cũng như bất cứ điều gì. Tôi cũng làm P ...
Khám phá và đọc thêm bài viết từ Avi AryanAvi Aryan
Thưởng thức bài viết này?
Để lại một cái thích và bình luận cho AVIAvi