Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 11 additions & 113 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ pip install tls-client
# Examples
The syntax is inspired by [requests](https://github.com/psf/requests), so its very similar and there are only very few things that are different.

Example 1 - Preset:
Example - Preset:
```python
import tls_client

# `client_identifier` picks the browser profile to impersonate, e.g. chrome_120, firefox_120, safari_ios_17_0 or
# `client_identifier` picks the browser profile to impersonate, e.g. chrome_152, firefox_148, safari_ios_26_0 or
# okhttp4_android_13. Every supported name is listed in tls_client/settings.py; unknown names raise a ValueError.

session = tls_client.Session(
client_identifier="chrome_112",
client_identifier="chrome_152",
random_tls_extension_order=True
)

Expand All @@ -30,116 +30,14 @@ res = session.get(
)
```

Example 2 - Custom:
```python
import tls_client

session = tls_client.Session(
# None switches from a preset to the settings below. Without it they are all ignored.
client_identifier=None,
ja3_string="771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
h2_settings={
"HEADER_TABLE_SIZE": 65536,
"MAX_CONCURRENT_STREAMS": 1000,
"INITIAL_WINDOW_SIZE": 6291456,
"MAX_HEADER_LIST_SIZE": 262144
},
h2_settings_order=[
"HEADER_TABLE_SIZE",
"MAX_CONCURRENT_STREAMS",
"INITIAL_WINDOW_SIZE",
"MAX_HEADER_LIST_SIZE"
],
supported_signature_algorithms=[
"ECDSAWithP256AndSHA256",
"PSSWithSHA256",
"PKCS1WithSHA256",
"ECDSAWithP384AndSHA384",
"PSSWithSHA384",
"PKCS1WithSHA384",
"PSSWithSHA512",
"PKCS1WithSHA512",
],
supported_versions=["GREASE", "1.3", "1.2"],
key_share_curves=["GREASE", "X25519"],
cert_compression_algo="brotli",
# The JA3 contains ALPN (16) and ALPS (17513), these fill them in.
alpn_protocols=["h2", "http/1.1"],
alps_protocols=["h2"],
pseudo_header_order=[
":method",
":authority",
":scheme",
":path"
],
connection_flow=15663105,
header_order=[
"accept",
"user-agent",
"accept-encoding",
"accept-language"
]
)

res = session.post(
"https://www.example.com/",
headers={
"key1": "value1",
},
json={
"key1": "key2"
}
)
```

Example 3 - Async:
```python
import asyncio
import tls_client

async def main():
# Takes the same arguments as tls_client.Session.
async with tls_client.AsyncSession(client_identifier="chrome_120") as session:
responses = await asyncio.gather(
*(session.get(f"https://www.example.com/{i}") for i in range(10))
)
print([res.status_code for res in responses])

asyncio.run(main())
```

# Pyinstaller / Pyarmor
**If you want to pack the library with Pyinstaller or Pyarmor, make sure to add this to your command:**

Linux with glibc (e.g. Ubuntu, Debian) / x86_64:
```
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-linux-ubuntu-amd64.so:tls_client/dependencies'
```

Linux / ARM64:
```
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-linux-arm64.so:tls_client/dependencies'
```

macOS on Intel x86 based Macs (pre-M1):
```
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-darwin-amd64.dylib:tls_client/dependencies'
```

macOS on Apple Silicon (M1 and later):
```
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-darwin-arm64.dylib:tls_client/dependencies'
```

Windows / 64-bit:
```
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-windows-64.dll;tls_client/dependencies'
```

Windows / 32-bit:
```
--add-binary '{path_to_library}/tls_client/dependencies/tls-client-windows-32.dll;tls_client/dependencies'
```
More examples:
- [Custom TLS fingerprint](https://github.com/FlorianREGAZ/Python-Tls-Client/blob/main/examples/example2%20-%20custom.py)
- [Certificate pinning](https://github.com/FlorianREGAZ/Python-Tls-Client/blob/main/examples/example3-%20certificate%20pinning.py)
- [File upload](https://github.com/FlorianREGAZ/Python-Tls-Client/blob/main/examples/example4%20-%20file%20upload.py)
- [Async](https://github.com/FlorianREGAZ/Python-Tls-Client/blob/main/examples/example5%20-%20async.py)
- [Redirects and cookies](https://github.com/FlorianREGAZ/Python-Tls-Client/blob/main/examples/example6%20-%20redirects%20and%20cookies.py)
- [Downloads and encodings](https://github.com/FlorianREGAZ/Python-Tls-Client/blob/main/examples/example7%20-%20download.py)
- [Multiprocessing](https://github.com/FlorianREGAZ/Python-Tls-Client/blob/main/examples/example8%20-%20multiprocessing.py)

# Acknowledgements
Big shout out to [Bogdanfinn](https://github.com/bogdanfinn) for open sourcing his [tls-client](https://github.com/bogdanfinn/tls-client) in Golang.
Expand Down
4 changes: 2 additions & 2 deletions examples/example1 - preset.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import tls_client

# `client_identifier` picks the browser profile to impersonate, e.g. chrome_120, firefox_120, safari_ios_17_0 or
# `client_identifier` picks the browser profile to impersonate, e.g. chrome_152, firefox_148, safari_ios_26_0 or
# okhttp4_android_13. Every supported name is listed in tls_client/settings.py; unknown names raise a ValueError.

session = tls_client.Session(client_identifier="chrome_112", random_tls_extension_order=True)
session = tls_client.Session(client_identifier="chrome_152", random_tls_extension_order=True)

res = session.get(
"https://www.example.com/",
Expand Down
3 changes: 2 additions & 1 deletion examples/example3- certificate pinning.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import tls_client

session = tls_client.Session(
client_identifier="chrome_152",
certificate_pinning={
"example.com": [
"NQvyabcS99nBqk/nZCUF44hFhshrkvxqYtfrZq3i+Ww=",
"4a6cdefI7OG6cuDZka5NDZ7FR8a60d3auda+sKfg4Ng=",
"x4QzuiC810K5/cMjb05Qm4k3Bw5zBn4lTdO/nEW/Td4=",
]
}
},
)

res = session.get(
Expand Down
2 changes: 1 addition & 1 deletion examples/example4 - file upload.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import tls_client

session = tls_client.Session(client_identifier="chrome_120")
session = tls_client.Session(client_identifier="chrome_152")

with open("report.pdf", "rb") as f:
res = session.post(
Expand Down
13 changes: 13 additions & 0 deletions examples/example5 - async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import asyncio

import tls_client


async def main() -> None:
# Takes the same arguments as tls_client.Session.
async with tls_client.AsyncSession(client_identifier="chrome_152") as session:
responses = await asyncio.gather(*(session.get(f"https://httpbin.org/get?page={i}") for i in range(10)))
print([res.status_code for res in responses])


asyncio.run(main())
15 changes: 15 additions & 0 deletions examples/example6 - redirects and cookies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import tls_client

session = tls_client.Session(client_identifier="chrome_152")

# Redirects are only followed with allow_redirects=True.
res = session.get("https://httpbin.org/cookies/set?flavour=chocolate", allow_redirects=True)
print(res.url) # https://httpbin.org/cookies
print([r.status_code for r in res.history]) # [302]

# Cookies set on any hop are kept in the session and sent with later requests.
print(session.cookies.get_dict()) # {'flavour': 'chocolate'}

# Cookies removed from the session are no longer sent.
session.cookies.clear()
print(session.get("https://httpbin.org/cookies").json()) # {'cookies': {}}
14 changes: 14 additions & 0 deletions examples/example7 - download.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import tls_client

session = tls_client.Session(client_identifier="chrome_152")

# .content is the exact bytes the server sent. timeout is in seconds and can be a float.
res = session.get("https://httpbin.org/image/png", timeout=10)
with open("image.png", "wb") as f:
f.write(res.content)

# .text is decoded with the charset from the Content-Type header or the page itself.
# Set .encoding when a page declares the wrong one.
res = session.get("https://httpbin.org/html", timeout=2.5)
res.encoding = "iso-8859-1"
print(res.text[:100])
18 changes: 18 additions & 0 deletions examples/example8 - multiprocessing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import multiprocessing

import tls_client


def fetch(url: str) -> int:
session = tls_client.Session(client_identifier="chrome_152")
return session.get(url).status_code


if __name__ == "__main__":
session = tls_client.Session(client_identifier="chrome_152")
print(session.get("https://www.example.com/").status_code)

# The parent has made a request, so the native library is loaded and a fork()ed child would hang.
# Use "spawn" or "forkserver" instead.
with multiprocessing.get_context("spawn").Pool(4) as pool:
print(pool.map(fetch, ["https://www.example.com/"] * 8))
Loading