3.3 表单提交与登录 Scrapy 高级主题:表单提交与登录 3.3 表单提交与登录 3.3.1 理解表单提交与登录原理 登录过程通常涉及以下几个步骤: 获取登录页面: 爬虫首先需要访问登录页面,获取包含登录表单的 HTML 内容。 分析表单结构: 分析 HTML 页面,找到表单的 属性(提交的 URL)和 标签的 属性(表单字段名称)。 构造表单数据: 根据找到的表单字段名称,构建包含用户名、密码等信息的表单数据。 提交表单: 使用 Scrapy 的 发送 POST 请求,将表单数据提交到登录 URL。 验证登录状态: 检查服务器返回的响应,判断登录是否成功。通常可以通过检查 Cookie 或特定页面内容来判断。
登录过程通常涉及以下几个步骤:
获取登录页面: 爬虫首先需要访问登录页面,获取包含登录表单的 HTML 内容。
分析表单结构: 分析 HTML 页面,找到表单的 action 属性(提交的 URL)和 input 标签的 name 属性(表单字段名称)。
构造表单数据: 根据找到的表单字段名称,构建包含用户名、密码等信息的表单数据。
提交表单: 使用 Scrapy 的 FormRequest 发送 POST 请求,将表单数据提交到登录 URL。
验证登录状态: 检查服务器返回的响应,判断登录是否成功。通常可以通过检查 Cookie 或特定页面内容来判断。
访问受保护页面: 如果登录成功,则可以访问需要登录才能访问的页面,并抓取数据。
可以用如下的mermaid图表示:
FormRequest 提交表单Scrapy 提供了 FormRequest 类,专门用于处理表单提交。FormRequest 继承自 Request 类,并添加了处理表单数据的相关功能。
FormRequest 的主要参数:
url:表单提交的 URL。
formdata:包含表单数据的字典。
callback:处理响应的回调函数。
method:HTTP 方法,默认为 'POST'。
headers:自定义请求头。
cookies:自定义 Cookie。
示例代码:
假设有一个简单的登录表单,包含用户名(username)和密码(password)两个字段,提交 URL 为 https://example.com/login。以下代码演示了如何使用 FormRequest 提交表单:
import scrapy class LoginSpider(scrapy.Spider): name = "login_spider" start_urls = ["https://example.com/login"] # 替换为实际的登录页面 URL def parse(self, response): # 提取 form 表单的 csrf token (如果存在) csrf_token = response.css('input[name="csrfmiddlewaretoken"]::attr(value)').get() # 构造表单数据 formdata = { 'username': 'your_username', # 替换为你的用户名 'password': 'your_password', # 替换为你的密码 'csrfmiddlewaretoken': csrf_token if csrf_token else '' # 如果页面有csrf token } # 发送 FormRequest yield scrapy.FormRequest( url='https://example.com/login', # 替换为实际的登录 URL formdata=formdata, callback=self.after_login, headers={'Referer': response.url} # 建议添加 Referer header ) def after_login(self, response): # 检查登录是否成功 if "欢迎" in response.text: # 替换为判断登录成功的依据 self.log("登录成功!") # 可以访问受保护页面并抓取数据 yield scrapy.Request(url='https://example.com/protected_page', callback=self.parse_protected_page) else: self.log("登录失败!") # 处理登录失败的情况 def parse_protected_page(self, response): # 从受保护页面提取数据 # 例如: # items = response.css('div.item::text').getall() # yield {'items': items} pass
代码解析:
start_urls 定义了爬虫启动时要访问的 URL,这里设置为登录页面。
parse 方法用于处理登录页面的响应。
在 parse 方法中,首先构造包含用户名、密码的表单数据。
然后,使用 scrapy.FormRequest 创建一个表单请求,指定了提交 URL、表单数据和回调函数 after_login。
after_login 方法用于处理登录后的响应。
在 after_login 方法中,首先检查登录是否成功,这里通过判断响应内容是否包含 "欢迎" 字符串来判断。
如果登录成功,则可以访问受保护页面并抓取数据。
parse_protected_page 方法用于从受保护页面提取数据。
注意:
需要将 your_username 和 your_password 替换为实际的用户名和密码。
判断登录成功的依据需要根据实际网站的响应内容进行调整。
很多网站会使用 CSRF token 来防止跨站请求伪造攻击。需要在登录页面提取 CSRF token,并将其包含在表单数据中。
Cookie 是服务器用来跟踪用户状态的一种机制。登录成功后,服务器会设置 Cookie,后续请求需要携带这些 Cookie 才能保持登录状态。
Scrapy 会自动处理 Cookie。当服务器设置 Cookie 时,Scrapy 会将其保存,并在后续请求中自动携带。
查看 Cookie:
可以使用 response.headers.getlist('Set-Cookie') 查看服务器设置的 Cookie。
自定义 Cookie:
可以使用 Request 对象的 cookies 参数自定义 Cookie。
示例代码:
import scrapy class CookieSpider(scrapy.Spider): name = "cookie_spider" start_urls = ["https://example.com/"] def start_requests(self): # 设置自定义 Cookie yield scrapy.Request( url='https://example.com/', cookies={'my_cookie': 'my_value'}, callback=self.parse ) def parse(self, response): # 处理响应 self.log(f"Received response from {response.url}") self.log(f"Cookies: {response.request.headers.getlist('Cookie')}")
代码解析:
start_requests 方法用于生成初始请求。
在 start_requests 方法中,使用 cookies 参数设置自定义 Cookie。
parse 方法用于处理响应,并打印请求头中的 Cookie。
scrapy.http.cookiejar.CookieJar 管理 CookieScrapy 提供了 scrapy.http.cookiejar.CookieJar 类,用于更精细地管理 Cookie。可以使用 CookieJar 对象来保存和加载 Cookie,以便在不同的请求之间共享 Cookie。
示例代码:
import scrapy from scrapy.http.cookiejar import CookieJar class CookieJarSpider(scrapy.Spider): name = "cookiejar_spider" start_urls = ["https://example.com/"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.cookie_jar = CookieJar() def start_requests(self): # 从 CookieJar 加载 Cookie for cookie in self.cookie_jar: yield scrapy.Request( url='https://example.com/', cookies={cookie.name: cookie.value}, callback=self.parse ) # 如果 CookieJar 为空,则发送初始请求 if not list(self.cookie_jar): yield scrapy.Request( url='https://example.com/', callback=self.parse ) def parse(self, response): # 保存 Cookie 到 CookieJar for cookie in response.headers.getlist('Set-Cookie'): self.cookie_jar.extract_cookies(response, response.request) # 处理响应 self.log(f"Received response from {response.url}") self.log(f"Cookies: {response.request.headers.getlist('Cookie')}")
代码解析:
在 __init__ 方法中,创建一个 CookieJar 对象。
在 start_requests 方法中,首先从 CookieJar 加载 Cookie,并使用这些 Cookie 发送请求。
如果 CookieJar 为空,则发送初始请求。
在 parse 方法中,将响应中的 Cookie 保存到 CookieJar。
有些网站在登录成功后会进行重定向。Scrapy 默认会自动处理重定向。
禁用重定向:
可以使用 dont_filter=True 参数禁用重定向。
示例代码:
import scrapy class RedirectSpider(scrapy.Spider): name = "redirect_spider" start_urls = ["https://example.com/login"] def parse(self, response): # 禁用重定向 yield scrapy.Request( url='https://example.com/login', callback=self.after_login, dont_filter=True ) def after_login(self, response): # 处理响应 self.log(f"Received response from {response.url}")
代码解析:
在 parse 方法中,使用 dont_filter=True 参数禁用重定向。
after_login 方法用于处理响应。
可以使用 Scrapy 中间件来统一处理登录逻辑。这样可以避免在每个 Spider 中都编写重复的登录代码。
示例代码:
# middlewares.py from scrapy import signals from scrapy.http import FormRequest class LoginMiddleware: def __init__(self, username, password, login_url): self.username = username self.password = password self.login_url = login_url @classmethod def from_crawler(cls, crawler): username = crawler.settings.get('LOGIN_USERNAME') password = crawler.settings.get('LOGIN_PASSWORD') login_url = crawler.settings.get('LOGIN_URL') middleware = cls(username, password, login_url) crawler.signals.connect(middleware.spider_opened, signal=signals.spider_opened) return middleware def spider_opened(self, spider): self.spider = spider def process_spider_input(self, response, spider): return None def process_spider_output(self, response, result, spider): for i in result: yield i def process_spider_exception(self, response, exception, spider): pass def process_start_requests(self, start_requests, spider): for request in start_requests: yield request def process_request(self, request, spider): # 如果需要登录,则先登录 if getattr(spider, 'requires_login', False) and not self.is_logged_in(request, spider): self.spider.logger.info("需要登录,正在尝试登录...") return FormRequest( url=self.login_url, formdata={'username': self.username, 'password': self.password}, callback=self.check_login, meta={'original_request': request} ) return None def check_login(self, response): # 检查登录是否成功 if "欢迎" in response.text: # 替换为判断登录成功的依据 self.spider.logger.info("登录成功!") original_request = response.meta['original_request'] yield original_request else: self.spider.logger.error("登录失败!") def is_logged_in(self, request, spider): # 检查是否已经登录,例如通过检查 Cookie # 具体实现需要根据网站的登录机制进行调整 return False # 示例中直接返回 False, 需要根据实际情况判断
# settings.py BOT_NAME = 'login_example' SPIDER_MODULES = ['login_example.spiders'] NEWSPIDER_MODULE = 'login_example.spiders' # 替换为你的用户名和密码 LOGIN_USERNAME = 'your_username' LOGIN_PASSWORD = 'your_password' LOGIN_URL = 'https://example.com/login' # 启用中间件 SPIDER_MIDDLEWARES = { 'login_example.middlewares.LoginMiddleware': 543, }
# spiders/example_spider.py import scrapy class ExampleSpider(scrapy.Spider): name = "example" start_urls = ['https://example.com/protected_page'] # 受保护的页面 requires_login = True # 标记这个spider需要登录 def parse(self, response): # 从受保护页面提取数据 self.logger.info("正在解析受保护页面...") # 例如: # items = response.css('div.item::text').getall() # yield {'items': items} pass
代码解析:
LoginMiddleware 类实现了登录中间件。
from_crawler 方法用于从 Scrapy 设置中获取用户名、密码和登录 URL。
process_request 方法在每个请求发送之前被调用。
在 process_request 方法中,首先检查 Spider 是否需要登录。
如果需要登录,则发送登录请求,并将原始请求保存到 meta 中。
check_login 方法用于检查登录是否成功。
如果登录成功,则将原始请求重新发送。
is_logged_in 函数需要根据实际网站的登录机制进行调整,判断是否已经登录。
使用中间件:
在 settings.py 中配置 LOGIN_USERNAME、LOGIN_PASSWORD 和 LOGIN_URL。
启用 LoginMiddleware 中间件。
在需要登录的 Spider 中,设置 requires_login = True。
本文详细介绍了 Scrapy 中表单提交与登录的相关代码实践,包括使用 FormRequest 提交表单、处理 Cookie、处理重定向和使用中间件处理登录。掌握这些技术可以帮助我们模拟用户的行为,成功登录并抓取需要登录才能访问的数据。
在实际应用中,需要根据具体网站的登录机制进行调整。例如,有些网站会使用 JavaScript 生成的验证码,或者使用更复杂的反爬虫机制。需要根据实际情况选择合适的解决方案。