Introduction
Automated ticket booking has become a popular use case for web scraping and browser automation. In this article, we explore how to combine Python’s Requests library and Selenium to automate the process of booking train tickets on 12306, China’s official railway ticketing platform. We’ll walk through the technical architecture, code implementation, and best practices for building a robust automation script.
Why Automate Ticket Booking?
Manual ticket booking on high-demand routes can be frustrating due to time constraints and rapid sell-outs. Automation allows you to monitor ticket availability, submit booking requests, and handle CAPTCHA challenges programmatically. This guide focuses on the 12306 platform, but the principles apply to any web-based booking system.
Technical Stack Overview
We use two primary tools:
- Requests: For sending HTTP requests directly to the 12306 API to query train schedules and submit booking data without loading the full web page.
- Selenium: For browser automation to handle dynamic content, CAPTCHA solving, and user interactions that are difficult to replicate with raw HTTP requests.
The combination allows us to leverage the speed of API calls while falling back to Selenium for complex UI tasks.
Setting Up the Environment
Before writing code, ensure you have Python installed along with the required libraries:
pip install requests selenium webdriver-manager
You’ll also need a WebDriver (e.g., ChromeDriver) for Selenium. The webdriver-manager package simplifies driver management.
Step 1: Querying Train Information with Requests
12306 exposes a RESTful API for querying train schedules. We can use Requests to fetch available trains between two stations on a given date. Here’s an example:
import requests
import json
def query_trains(from_station, to_station, date):
url = "https://kyfw.12306.cn/otn/leftTicket/queryZ"
params = {
"leftTicketDTO.train_date": date,
"leftTicketDTO.from_station": from_station,
"leftTicketDTO.to_station": to_station,
"purpose_codes": "ADULT"
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
}
response = requests.get(url, params=params, headers=headers)
if response.status_code == 200:
data = response.json()
# Parse and return train list
return data.get("data", {}).get("result", [])
else:
print("Failed to query trains")
return []
Note: Station codes (e.g., “BJP” for Beijing) must be used instead of names. You can obtain these from 12306’s station name API.
Step 2: Automating Login with Selenium
Booking requires authentication. Selenium can automate the login process, including handling CAPTCHAs. Here’s a basic login flow:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
def login_12306(username, password):
driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://kyfw.12306.cn/otn/login/init")
# Wait for username field and input credentials
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "username"))).send_keys(username)
driver.find_element(By.ID, "password").send_keys(password)
# Handle CAPTCHA (manual intervention or external service)
# For simplicity, we assume manual CAPTCHA solving
input("Please solve the CAPTCHA in the browser and press Enter...")
# Click login button
driver.find_element(By.ID, "loginSub").click()
# Wait for redirect to main page
WebDriverWait(driver, 10).until(EC.url_contains("leftTicket"))
return driver
Important: CAPTCHA solving is a challenge. For production, consider integrating a third-party CAPTCHA solving service or using machine learning models.
Step 3: Submitting a Booking Request
Once logged in, we can use Selenium to navigate to the booking page and submit a request. Alternatively, we can extract session cookies from Selenium and use them with Requests for subsequent API calls. The latter is more efficient:
def get_cookies(driver):
cookies = driver.get_cookies()
session = requests.Session()
for cookie in cookies:
session.cookies.set(cookie['name'], cookie['value'])
return session
# After login
session = get_cookies(driver)
# Now use session to make booking API calls
With the session, you can submit a booking request to the 12306 API, specifying train number, seat type, and passenger details.
Step 4: Handling Dynamic Content and Anti-Bot Measures
12306 employs various anti-bot techniques, including token validation and request rate limiting. To mitigate this:
- Use realistic user-agent headers.
- Introduce random delays between requests.
- Rotate IP addresses if necessary (via proxies).
- Keep Selenium interactions minimal to avoid detection.
Complete Workflow Integration
Here’s a high-level integration of the steps:
def main():
# Step 1: Query trains
trains = query_trains("BJP", "SHH", "2024-12-01")
if not trains:
print("No trains available")
return
# Step 2: Login
driver = login_12306("your_username", "your_password")
session = get_cookies(driver)
# Step 3: Select a train and submit booking
# (Implementation depends on specific API endpoints)
driver.quit()
Best Practices and Ethical Considerations
Automated booking should be used responsibly. Avoid overwhelming the server with rapid requests. Respect the platform’s terms of service. For personal use, this script can save time, but commercial use may require permission.
Conclusion
Combining Requests and Selenium provides a powerful toolkit for automating web tasks like ticket booking. While the 12306 platform presents unique challenges, the approach outlined here can be adapted to other websites. Experiment with the code, handle edge cases, and always test in a development environment first.