一个文件就写出一个selector

本文介绍了一种简化Android中Selector配置的方法,仅使用一个XML文件即可实现不同的状态样式切换,包括按钮按下状态和默认状态的视觉效果。

以前写selector都要三个文件,今天发现用一个文件就可以实现:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape>
            <solid android:color="#f2f2f2" />
            <corners android:bottomLeftRadius="5dp" android:bottomRightRadius="5dp" android:topLeftRadius="5dp" android:topRightRadius="5dp" />
            <stroke android:width="1px" android:color="#ffd1d1d1" />
        </shape>
    </item>
    <item>
        <shape>
            <solid android:color="#ffffff" />
            <corners android:bottomLeftRadius="2.5dp" android:bottomRightRadius="2.5dp" android:topLeftRadius="2.5dp" android:topRightRadius="2.5dp" />
            <stroke android:width="0.5px" android:color="#ffd1d1d1" />
        </shape>
    </item>
</selector>


<think>我们参考用户提供的引用和之前的问题,来编写一个爬取小说网站内容的爬虫代码。 根据引用[1],我们可以定义一个爬虫类NovelSpider,它包含初始化方法,设置URL、请求头等。 引用[2]提到,我们可以从小说主页获取章节列表,然后分别爬取每个章节的内容。 引用[3]提到使用requests库。 步骤: 1. 定义NovelSpider类,初始化时传入小说目录页的URL(即章节列表页),以及需要爬取的页数(如果分页的话)。 2. 编写获取章节列表的方法:请求目录页,解析出所有章节的链接和标题。 3. 编写爬取单个章节内容的方法:请求章节链接,解析出正文内容。 4. 编写保存内容的方法:将每个章节的内容保存到本地文件(如txt)。 注意:由于网站结构不同,解析规则需要根据目标网站调整。这里以逐浪小说网为例(引用[2]),但实际代码需要根据具体网站调整。 我们将按照以下步骤编写代码: - 导入requests和BeautifulSoup - 定义NovelSpider类 - __init__: 初始化URL、headers、page(分页参数)等 - get_chapter_list: 获取章节列表(标题和链接) - get_chapter_content: 根据章节链接获取内容 - download_all: 下载所有章节并保存 由于引用[1]中提到了分页参数,我们假设目录页有分页,那么需要处理多个目录页。 但是,请注意:不同的网站分页机制不同,这里我们假设分页URL规则为在基础URL后加页数。 由于用户没有提供具体网站,我们将以通用结构为例,但实际使用时需要调整解析规则。 代码示例(以逐浪小说网为例):</think>根据引用内容,我将为您编写一个完整的Python爬虫类,用于爬取小说网站内容。这个实现基于引用[1][2][3]中的最佳实践,包含完整的错误处理和反爬策略: ```python import requests from bs4 import BeautifulSoup import time import random import re import os from urllib.parse import urljoin class NovelSpider: """小说爬虫类,用于爬取小说网站内容""" def __init__(self, base_url, start_page=1, end_page=2, output_dir="novels"): """ 初始化爬虫 :param base_url: 小说目录页基础URL (需包含页码占位符{page}) :param start_page: 起始页码 :param end_page: 结束页码 :param output_dir: 小说保存目录 """ self.base_url = base_url self.start_page = start_page self.end_page = end_page self.output_dir = output_dir self.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', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Referer': 'https://www.google.com/' } self.session = requests.Session() self.session.headers.update(self.headers) # 创建输出目录 os.makedirs(output_dir, exist_ok=True) def _request_with_retry(self, url, max_retries=3): """带重试机制的请求函数""" for attempt in range(max_retries): try: response = self.session.get(url, timeout=10) response.raise_for_status() # 检查是否被重定向到验证页面 if "verify" in response.url or "captcha" in response.url: raise Exception("触发反爬验证") return response except Exception as e: print(f"请求失败 ({attempt+1}/{max_retries}): {str(e)}") time.sleep(2 ** attempt) # 指数退避 return None def parse_chapter_list(self, html_content): """解析章节列表页,提取章节链接和标题""" soup = BeautifulSoup(html_content, 'html.parser') chapters = [] # 常见章节选择器模式(根据实际网站调整) selectors = [ 'div.chapter-list li a', # 通用选择器1 'ul.chapter > li > a', # 通用选择器2 'div.listmain dl dd a', # 针对笔趣阁类网站 'div#list dl dd a' # 针对顶点小说网等 ] for selector in selectors: links = soup.select(selector) if links: for link in links: # 过滤无效章节(广告等) if "广告" in link.text or "章" not in link.text: continue chapters.append({ 'title': link.text.strip(), 'url': urljoin(self.base_url, link['href']) }) break return chapters def parse_chapter_content(self, html_content): """解析章节内容页,提取正文""" soup = BeautifulSoup(html_content, 'html.parser') content = "" # 常见内容选择器模式(根据实际网站调整) content_selectors = [ 'div#content', # 通用内容容器 'div.read-content', # 起点等网站 'div.content', # 常见选择器 'div.article-body' # 长佩文学等 ] for selector in content_selectors: content_div = soup.select_one(selector) if content_div: # 清理不需要的元素 for elem in content_div.select('script, style, div.ads, div.advertisement'): elem.decompose() # 获取处理后的文本 content = content_div.get_text(separator="\n", strip=True) # 清理特殊字符和网站水印 content = re.sub(r'[\u3000\xa0]+', ' ', content) # 替换特殊空格 content = re.sub(r'请收藏.*|www\..*|请访问.*', '', content) break return content def download_chapter(self, chapter): """下载单个章节内容""" print(f"正在下载: {chapter['title']}") response = self._request_with_retry(chapter['url']) if not response or response.status_code != 200: print(f"章节下载失败: {chapter['title']}") return None content = self.parse_chapter_content(response.text) if not content: print(f"内容解析失败: {chapter['title']}") return None return { 'title': chapter['title'], 'content': content } def save_to_file(self, novel_title, chapters): """保存小说到文件""" filename = f"{novel_title}.txt".replace('/', '_') filepath = os.path.join(self.output_dir, filename) with open(filepath, 'w', encoding='utf-8') as f: f.write(f"《{novel_title}》\n\n") for chapter in chapters: f.write(f"\n\n{chapter['title']}\n\n") f.write(chapter['content']) print(f"\n小说已保存到: {filepath}") return filepath def get_novel_title(self, soup): """从页面解析小说标题""" # 常见标题选择器 title_selectors = [ 'div.book-info h1', # 起点模式 'div.info h2', # 晋江模式 'h1.title', # 通用模式 'div#info h1' # 笔趣阁模式 ] for selector in title_selectors: title_elem = soup.select_one(selector) if title_elem: return title_elem.text.strip() return "未知小说" def run(self): """执行爬虫主流程""" all_chapters = [] novel_title = "" # 遍历所有目录页 for page in range(self.start_page, self.end_page + 1): url = self.base_url.format(page=page) print(f"\n正在处理目录页: {url}") response = self._request_with_retry(url) if not response or response.status_code != 200: print(f"目录页请求失败: {url}") continue soup = BeautifulSoup(response.text, 'html.parser') # 获取小说标题(只在第一页获取) if page == self.start_page and not novel_title: novel_title = self.get_novel_title(soup) print(f"开始下载小说: 《{novel_title}》") # 解析章节列表 chapters = self.parse_chapter_list(response.text) if not chapters: print("未找到章节列表,请检查选择器") continue print(f"找到 {len(chapters)} 个章节") # 下载章节内容 for chapter in chapters: chapter_content = self.download_chapter(chapter) if chapter_content: all_chapters.append(chapter_content) # 随机延时避免被封 (1-3秒) time.sleep(random.uniform(1, 3)) # 保存小说 if all_chapters: return self.save_to_file(novel_title, all_chapters) return None # 使用示例 ============================================== if __name__ == "__main__": # 示例配置(需替换为实际网站URL) # 注意:URL中{page}会被替换为实际页码 spider = NovelSpider( base_url="https://www.example.com/novel/123/?page={page}", start_page=1, end_page=5, # 爬取前5页章节 output_dir="my_novels" ) # 执行爬取 saved_path = spider.run() if saved_path: print(f"\n爬取完成!小说保存位置: {saved_path}") else: print("爬取失败,请检查配置和网络连接") ``` ### 使用说明 1. **初始化参数**: - `base_url`:小说目录页URL(必须包含`{page}`占位符) - `start_page`/`end_page`:爬取的页码范围 - `output_dir`:小说保存目录 2. **核心功能**: - **智能解析**:自动适配常见网站结构(起点、晋江、笔趣阁等) - **反爬策略**: - 随机延时(1-3秒/请求) - 自动重试机制(指数退避) - 完整HTTP头模拟(User-Agent/Referer等) - **内容清洗**:自动移除广告、脚本和网站水印 - **错误处理**:网络错误、解析失败等场景的健壮处理 3. **自定义调整**: - 若爬取特定网站失败,可修改类中的`selectors`列表: ```python # 在parse_chapter_list方法中调整 selectors = [ 'div.custom-chapter-list a', # 添加自定义选择器 ... # 原有选择器 ] ``` ### 注意事项 1. **法律合规**: - 仅爬取**公开免费**内容 - 遵守网站`robots.txt`规则 - 避免商业用途 2. **反爬升级**: - 如遇封IP,可添加代理池: ```python proxies = {'http': 'http://user:pass@10.10.1.10:3128'} response = self.session.get(url, proxies=proxies) ``` - 动态网站考虑添加Selenium支持 3. **性能优化**: - 大规模爬取使用异步请求(`aiohttp`) - 分布式爬虫架构(Scrapy+Redis) > 请将示例URL替换为目标网站的实际URL,并根据网站结构调整选择器[^1][^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值