Python 爬虫入门:从抓取源码到模拟登录 (附代码详解)
你好,未来的爬虫工程师!
你是否曾惊叹于某些应用能聚合来自不同网站的信息?或者想自动从网页上收集数据用于分析、研究或仅仅是满足好奇心?这一切的背后,往往离不开一种强大的技术——网络爬虫 (Web Scraping)。
Python 因其丰富的库和简洁的语法,成为了开发网络爬虫的首选语言之一。这篇博客将带你一步步走进 Python 爬虫的世界,从最基础的网页抓取,到应对反爬虫策略,再到处理多页数据和模拟登录,让你对爬虫开发有一个全面的认识。
准备好了吗?让我们开始这段激动人心的旅程吧!
1. 第一步:获取网页源码与 Beautiful Soup 解析
万丈高楼平地起,爬虫的第一步就是获取目标网页的“骨架”——HTML 源代码。Python 的 requests
库能轻松模拟浏览器发送 HTTP 请求,拿到网页内容。
但拿到原始 HTML 还不够,我们需要从中提取有用的信息。这时,强大的 Beautiful Soup 4
(bs4) 库就派上用场了!它可以将复杂的 HTML 文档转换成 Python 对象,让我们像操作本地文件一样方便地查找标签、提取文本和属性。
核心概念:
requests.get(url)
: 发送 GET 请求获取网页。response.text
: 获取响应的文本内容(HTML 源码)。BeautifulSoup(html, 'html.parser')
: 创建 Beautiful Soup 对象来解析 HTML。soup.find('tag_name', attribute='value')
: 查找第一个符合条件的标签。soup.find_all('tag_name', class_='some_class')
: 查找所有符合条件的标签。.get_text(strip=True)
: 获取标签内的文本,strip=True
去除多余空白。
抓取名言警句
import requests
from bs4 import BeautifulSoup
import csv
# 目标网站 (一个练习用的网站)
url = 'http://quotes.toscrape.com/'
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'} # 伪装一下浏览器
try:
print(f"开始抓取: {url}")
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # 检查请求是否成功
response.encoding = response.apparent_encoding # 自动设置编码
html_content = response.text
# 使用 Beautiful Soup 解析
soup = BeautifulSoup(html_content, 'html.parser')
quotes_data = []
quote_divs = soup.find_all('div', class_='quote') # 找到所有包含名言的 div
print(f"找到 {len(quote_divs)} 条名言,开始解析...")
for quote_div in quote_divs:
text = quote_div.find('span', class_='text').get_text(strip=True)
author = quote_div.find('small', class_='author').get_text(strip=True)
tags = ', '.join([tag.get_text(strip=True) for tag in quote_div.find_all('a', class_='tag')])
quotes_data.append({'text': text, 'author': author, 'tags': tags})
print(f" - {text} - {author}")
# 保存到 CSV 文件
if quotes_data:
filename = 'quotes.csv'
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
writer = csv.DictWriter(f, fieldnames=['text', 'author', 'tags'])
writer.writeheader()
writer.writerows(quotes_data)
print(f"\n数据已保存到 {filename}")
except requests.exceptions.RequestException as e:
print(f"抓取出错: {e}")
except Exception as e:
print(f"处理出错: {e}")
代码解释:
- 我们使用
requests.get
获取网页,并传入headers
伪装成浏览器。 response.raise_for_status()
确保我们得到了成功的响应 (状态码 200 OK)。BeautifulSoup
解析 HTML。soup.find_all('div', class_='quote')
定位到所有包含名言信息的div
块。- 在每个
div
块内部,我们继续使用find
找到具体的span
(文本) 和small
(作者) 标签,并用get_text()
提取内容。 - 最后,使用
csv
模块将提取到的数据写入 CSV 文件,方便后续使用。
2. 升级装备:爬虫伪装术
直接用 Python 脚本访问网站,很容易被识别为“机器人”。为了让我们的爬虫更像一个普通用户,我们需要一些伪装技巧:
- 浏览器伪装 (User-Agent): 在请求头 (Headers) 中加入
User-Agent
字段,告诉服务器“我是一个正常的浏览器”。你可以在浏览器的开发者工具 (F12) -> Network -> Headers 中找到你自己的 User-Agent。 - 设置时间间隔 (Rate Limiting): 别像机关枪一样疯狂请求!在每次请求之间加入
time.sleep(秒数)
,模拟人类的浏览速度,减轻服务器压力,也降低被封 IP 的风险。 - 代理 IP (Proxy): 如果你的 IP 因为请求过于频繁被封了,或者你想模拟不同地区的访问,可以使用代理 IP。
requests
库支持通过proxies
参数使用代理。
** 添加延迟和代理结构:**
import requests
import time
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
}
# 代理 IP (需要替换为有效的代理地址)
proxies = {
'http': 'http://YOUR_PROXY_IP:PORT',
'https': 'http://YOUR_PROXY_IP:PORT',
}
url = 'http://quotes.toscrape.com/page/2/' # 尝试访问第二页
try:
print("等待 1.5 秒...")
time.sleep(1.5) # 请求前暂停
# 发送请求,带上 Headers,如果需要代理,加上 proxies=proxies
# response = requests.get(url, headers=headers, proxies=proxies, timeout=15)
response = requests.get(url, headers=headers, timeout=10) # 这里暂时不用代理
response.raise_for_status()
print(f"成功访问 (带伪装): {url}")
# ... 后续解析代码 ...
except requests.exceptions.ProxyError as e:
print(f"代理错误: {e}")
except requests.exceptions.RequestException as e:
print(f"请求错误: {e}")
关键点: 伪装是爬虫与反爬策略博弈的第一步,让你的爬虫活得更久一点。
3. 应对反爬虫策略
网站方为了保护数据和服务器,会设置各种“关卡”来阻止爬虫,这就是反爬虫。常见的策略有:
- User-Agent 检测: 拒绝非浏览器标识。(应对:伪装 User-Agent)
- IP 频率限制/封禁: 单位时间内请求次数过多。(应对:加延迟
time.sleep()
,使用代理 IP 池) - 验证码 (CAPTCHA): 需要人机交互验证。(应对:难点!可能需要接入第三方打码平台,或尝试 OCR 识别,或寻找无需验证码的接口)
- 动态加载 (JavaScript/AJAX): 数据由 JS 在浏览器生成,直接看 HTML 源码可能不全。(应对:分析浏览器 F12 Network 中的 XHR/Fetch 请求找到数据接口,或使用
Selenium
/Playwright
模拟浏览器行为) - 登录验证: 核心数据需要登录。(应对:模拟登录,见第 5 点)
- Headers 检测: 检查 Referer, Cookie 等。(应对:尽量模拟完整请求头)
应对核心:
- 像人一样访问: 合理的频率、完整的 Headers。
- 遵守规则: 查看网站的
robots.txt
文件 (通常在网站根目录,如www.example.com/robots.txt
),了解网站允许爬取的范围。 - 仔细分析: 打开浏览器开发者工具 (F12),切换到 Network (网络) 标签页,观察页面加载时发出了哪些请求,特别是 XHR/Fetch 类型的请求,它们往往是获取动态数据的关键。
示例:找到数据接口 (概念)
如果你发现数据是通过 AJAX 加载的,可以尝试直接请求那个接口:
# 假设通过 F12 发现数据接口 URL
api_url = 'http://example.com/api/get_data?category=news'
headers = {'User-Agent': '...', 'X-Requested-With': 'XMLHttpRequest'} # 有些 API 需要这个
try:
response = requests.get(api_url, headers=headers)
response.raise_for_status()
json_data = response.json() # API 通常返回 JSON
print("成功从 API 获取数据:", json_data)
# ... 处理 json_data ...
except requests.exceptions.RequestException as e:
print(f"请求 API 失败: {e}")
except requests.exceptions.JSONDecodeError:
print("解析 JSON 失败")
示例:使用 Selenium (概念)
对于复杂的 JS 渲染页面,Selenium
可以驱动一个真实的浏览器来加载页面:
# 需要安装 selenium 和 webdriver_manager
# pip install selenium webdriver-manager
# from selenium import webdriver
# from selenium.webdriver.common.by import By
# from selenium.webdriver.chrome.service import Service
# from webdriver_manager.chrome import ChromeDriverManager
# import time
# options = webdriver.ChromeOptions()
# options.add_argument('--headless') # 无头模式运行
# service = Service(ChromeDriverManager().install())
# driver = webdriver.Chrome(service=service, options=options)
# try:
# driver.get("一个 JS 渲染的复杂页面 URL")
# time.sleep(3) # 等待 JS 执行完毕 (实际应用中用更智能的等待)
# content = driver.find_element(By.ID, 'dynamic-content-id').text
# print("Selenium 获取到的内容:", content)
# finally:
# driver.quit()
记住: 反爬虫技术日新月异,没有一劳永逸的方法,持续学习和分析是关键。
4. 深入探索:单页与多页数据爬取
网站的内容通常不会放在一个页面上。我们需要让爬虫能够“翻页”或“点击链接”来获取更多数据。
- 页码变更: 观察列表页 URL 的变化规律。通常是
?page=2
,&p=3
这样的参数,或者/list/page/2/
这样的路径。找到规律后,用循环生成不同的 URL 即可。 - 超链接跟随: 从当前页面提取所有指向目标页面(如详情页)的链接 (
<a>
标签的href
属性),然后依次访问这些链接。注意处理相对路径 (/details/123
) 和绝对路径 (http://...
),urllib.parse.urljoin
可以帮你拼接。
爬取多个分页
import requests
from bs4 import BeautifulSoup
import time
from urllib.parse import urljoin
base_url = 'http://quotes.toscrape.com'
headers = {'User-Agent': '...'} # 保持 User-Agent
all_quotes = []
max_pages_to_crawl = 3 # 设置最大爬取页数
print("开始爬取多个分页...")
current_url = base_url + '/'
for page_num in range(1, max_pages_to_crawl + 1):
print(f"--- 正在处理页面: {current_url} ---")
try:
time.sleep(1) # 友好访问
response = requests.get(current_url, headers=headers, timeout=10)
response.raise_for_status()
response.encoding = response.apparent_encoding
soup = BeautifulSoup(response.text, 'html.parser')
# 提取当前页数据 (同第一部分)
quote_divs = soup.find_all('div', class_='quote')
for quote_div in quote_divs:
text = quote_div.find('span', class_='text').get_text(strip=True)
author = quote_div.find('small', class_='author').get_text(strip=True)
all_quotes.append({'text': text, 'author': author})
print(f" 抓取到: {text[:30]}... by {author}")
# 查找“下一页”的链接
next_li = soup.find('li', class_='next')
if next_li and next_li.find('a'):
next_page_relative_url = next_li.find('a')['href']
current_url = urljoin(base_url, next_page_relative_url) # 拼接成完整的下一页 URL
else:
print("找不到下一页,停止爬取。")
break # 没有下一页了,退出循环
except requests.exceptions.RequestException as e:
print(f"请求页面 {current_url} 失败: {e}")
break
except Exception as e:
print(f"处理页面 {current_url} 时出错: {e}")
print(f"\n多页爬取完成,共获取 {len(all_quotes)} 条名言。")
代码解释:
- 我们用一个
for
循环(或while
循环)来控制爬取的页数。 - 在每次循环中,请求当前页
current_url
。 - 解析数据后,使用
soup.find('li', class_='next')
找到包含“下一页”链接的<li>
元素。 - 如果找到了,提取
<a>
标签的href
属性(这通常是相对路径)。 - 使用
urljoin(base_url, next_page_relative_url)
将基础 URL 和相对路径拼接成下一页的完整 URL,更新current_url
,准备下一次循环。 - 如果找不到“下一页”按钮,说明到了最后一页,
break
退出循环。
5. 高级挑战:模拟登录 (仅用于学习)
郑重声明:模拟登录爬取 QQ、微博等大型社交网站数据,不仅技术难度极高,而且极有可能违反用户协议,导致账号被封禁,甚至可能涉及法律风险。本节仅作技术原理探讨,请勿用于非法用途!
有些数据需要登录后才能看到。模拟登录的核心是让服务器认为你的爬虫是一个已经登录的用户。
- 原理: 登录通常是向服务器发送一个 POST 请求,包含用户名、密码等信息。成功后,服务器会在响应中设置 Cookie(一小段身份信息)。后续访问其他页面时,带上这个 Cookie,服务器就知道你是谁了。
requests.Session
对象: 它是requests
库的利器!Session
对象会自动管理 Cookie。你只需要用同一个Session
对象发送所有请求(包括登录请求和后续的数据请求),它就能自动帮你处理 Cookie 的传递。- 挑战:
- 找到登录接口和参数: F12大法好!监控登录过程的网络请求,找到那个 POST 请求,看清它的 URL 和表单数据 (Form Data)。
- CSRF Token: 很多网站有这个安全令牌,通常在登录页的隐藏字段里,需要提取出来一起提交。
- 验证码: 图形、滑块... 非常棘手。
- JS 加密: 密码等信息可能在提交前被 JS 加密,需要逆向分析 JS 代码。
示例代码 (极其简化的概念模型):
import requests
from bs4 import BeautifulSoup
# 使用 Session 对象自动管理 Cookie
session = requests.Session()
session.headers.update({'User-Agent': '...'})
login_page_url = 'http://a-simple-login-site.com/login' # 假设的登录页
login_api_url = 'http://a-simple-login-site.com/do_login' # 假设的登录接口
protected_url = 'http://a-simple-login-site.com/dashboard' # 假设的登录后页面
try:
# 1. 访问登录页,获取 CSRF token (假设有)
print(f"访问登录页: {login_page_url}")
resp_page = session.get(login_page_url)
resp_page.raise_for_status()
soup_page = BeautifulSoup(resp_page.text, 'html.parser')
csrf_token = soup_page.find('input', {'name': 'csrf_token'})['value'] # 假设是这样获取
print(f"获取到 CSRF Token: {csrf_token}")
# 2. 准备登录数据
login_data = {
'username': 'my_username',
'password': 'my_password',
'csrf_token': csrf_token,
# ... 可能还有其他字段
}
# 3. 发送 POST 请求进行登录
print(f"发送登录请求到: {login_api_url}")
resp_login = session.post(login_api_url, data=login_data)
resp_login.raise_for_status()
# 4. 检查登录是否成功 (检查方式因网站而异)
if "登录成功" in resp_login.text or resp_login.url == protected_url:
print("登录成功!")
# 5. 访问需要登录的页面
print(f"访问受保护页面: {protected_url}")
resp_protected = session.get(protected_url)
resp_protected.raise_for_status()
print("成功获取受保护页面内容:")
# print(resp_protected.text[:200] + "...") # 可以打印部分内容看看
# ... 解析受保护页面的数据 ...
else:
print("登录失败,请检查。")
# print(resp_login.text) # 打印失败信息
except requests.exceptions.RequestException as e:
print(f"登录过程中出错: {e}")
except Exception as e:
print(f"发生错误: {e}") # 比如找不到 csrf token
再次强调: 模拟登录复杂且敏感,请务必在法律和道德允许的范围内,以学习为目的进行尝试。对于大型网站,优先考虑是否有官方 API。
总结与忠告
恭喜你!你已经了解了 Python 爬虫从入门到进阶的关键技术点:
- 获取与解析:
requests
+BeautifulSoup
是基础。 - 伪装与反反爬:
User-Agent
,time.sleep
, 代理 IP,以及分析动态加载是进阶。 - 多页处理: 掌握 URL 规律和链接提取。
- 模拟登录: 使用
requests.Session
,但要极其谨慎。
最后,请务必记住:
- 做负责任的爬虫开发者:
- 遵守
robots.txt
协议。 - 控制爬取频率, 不要给目标网站带来过大负担。
- 表明身份 (User-Agent), 如果可能。
- 遵守
- 尊重数据版权和隐私: 不要爬取、使用、传播受保护或私密的数据。
- 遵守法律法规和网站条款: 非法爬取可能带来严重后果。
网络爬虫是一个强大而有趣的工具,希望这篇博客能为你打开一扇新的大门。不断实践,不断学习,你也能成为一名出色的爬虫工程师!
Happy Scraping!