基于Python零基础制作一个自己的爬虫程序

此博客为一个详细的Python爬虫教程,从基础知识到完整实现,包括爬取网页内容、解析数据、存储数据、使用代理、反反爬策略等。稍后会提供完整的教程供你参考。

1. 爬虫基础

什么是爬虫: 网络爬虫(Web Crawler),又称网络蜘蛛(Spider),是一种自动化脚本或程序,用于按照一定规则批量获取网页数据。爬虫通过模拟浏览器行为向目标网站发送HTTP请求,获取网页的HTML源码,然后解析并提取所需的信息。简单来说,爬虫就是让计算机替代人工,自动从互联网上“爬”取数据。

基本概念: 爬虫运行时通常遵循以下流程:

  1. 发送请求: 爬虫指定一个URL,向网站服务器发送HTTP请求(如GET请求)。就像我们在浏览器中访问一个网址一样,服务器会返回该页面的内容。
  2. 获取响应: 服务器返回网页的HTML内容(以及可能的JSON、图片等资源)。爬虫程序接收响应数据,如果请求成功会得到状态码200以及页面内容。
  3. 解析内容: 爬虫从获取的HTML中提取目标数据。这需要对网页的结构有所了解,通过解析HTML来定位我们需要的信息(比如文章标题、价格、图片链接等)。
  4. 保存结果: 将提取的数据进行处理和存储,例如输出到屏幕、保存到文件或数据库,供后续分析和使用。

值得注意的是,网络爬虫可以小到针对单个网站的数据采集,也可以大到像搜索引擎那样抓取整个互联网的页面并建立索引。根据需求不同,爬虫可以是一次性的小脚本,也可以是持续运行的大规模系统。

合法性与道德问题: 在编写和运行爬虫时,需要注意法律法规和道德规范:

  • 遵守网站规则: 大多数网站都会在根目录提供一个robots.txt协议文件,里面声明了爬虫可以或不可以抓取的范围。爬虫应遵循该协议,不要爬取被禁止的页面,以体现对网站意愿的尊重(虽然robots.txt不是强制性的,但遵守它是业界的君子协议)。
  • 尊重使用条款: 仔细阅读目标网站的服务条款/使用政策。有些网站明确禁止未经授权的抓取行为,违反这些规定可能带来法律风险。在动手爬取之前,确保爬虫行为不违背目标站点的政策要求。
  • 避免扰乱服务: 控制爬虫的抓取频率和并发量,避免对目标服务器造成过大压力。过于频繁的请求可能被视作拒绝服务攻击(DDoS)行为,这既不道德也可能触发目标站的防御机制,导致爬虫被封禁。
  • 尊重隐私与版权: 不要抓取涉及个人隐私的数据(如个人邮箱、电话号码、住所等)或受版权保护的内容。在不少国家和地区,未授权获取他人受保护的数据可能违法。确保爬取的数据是公开信息,且用于合法用途。必要时,可以征得网站或数据所有者的授权。
  • 安全与责任: 当遇到需要登录、验证码(CAPTCHA)等明显的反爬措施时,要格外谨慎。这些通常表明网站希望保护其内容不被自动抓取。强行绕过这些机制可能违法,甚至触及刑事风险。此外,爬取到的数据要妥善保存和使用,防止泄露或滥用。

总之,负责任地爬取是每个开发者应遵循的准则。在获取数据的同时,确保自己的爬虫行为合法合规、不过度消耗公共资源,并尊重目标网站和用户的权益。

2. 环境准备

在开始编写爬虫之前,需要准备好Python编程环境并安装相关的依赖库。以下是在不同操作系统上设置Python爬虫环境的一般步骤:

  • 安装 Python: 如果尚未安装Python,请从Python官网下载适用于你操作系统的版本进行安装。建议使用Python 3.x版本。安装过程中记得将Python添加到环境变量(Windows系统)或确保在终端可以调用python3命令(macOS/Linux系统)。
  • 创建虚拟环境: 为了避免依赖混杂,建议为爬虫项目创建一个Python虚拟环境。可以使用内置的venv模块:
    python3 -m venv venv  # 创建名为venv的虚拟环境文件夹
    source venv/bin/activate  # 在Linux/macOS上激活虚拟环境
    .\venv\Scripts\activate   # 在Windows上激活虚拟环境
    
    激活虚拟环境后,命令行提示符通常会出现环境名称前缀,表示接下来安装的库都会只针对该环境。
  • 升级pip工具: 确保使用最新的pip包管理器,以便顺利安装依赖:
    python -m pip install --upgrade pip
    
  • 安装必备库: 爬虫常用的Python库包括:
    • requests:用于发送HTTP请求,获取网页内容。
    • beautifulsoup4(即bs4):用于方便地解析HTML/XML数据。
    • lxml:高性能的XML和HTML解析库,支持XPath查询。
    • Scrapy:功能强大的爬虫框架,适合构建大型爬虫项目。
    • selenium:浏览器自动化工具,能驱动浏览器执行JS,用于处理需要动态渲染的网站。
    可以使用pip一次性安装多个库,例如:
    pip install requests beautifulsoup4 lxml scrapy selenium
    
    (如果不需要用到某些高级功能,比如本教程后续提到的Scrapy框架或Selenium模拟浏览器,可以暂时不安装它们。)
  • 验证安装: 可以通过Python交互环境或脚本导入以上库来验证是否安装成功:
    import requests, bs4, lxml, scrapy, selenium
    print("All libraries installed successfully!")
    
    如果没有报错说明环境准备就绪。如果遇到某个库导入错误,可能是安装失败或者未加入虚拟环境,需检查并重新安装。
  • 开发工具选择: 你可以使用任意文本编辑器或IDE来编写爬虫代码,例如:VS Code、PyCharm、Jupyter Notebook等。IDE通常提供更好的调试和自动补全,有助于提高开发效率。
  • 其他依赖: 根据具体需求,可能还需安装数据库驱动(如pymysql用于连接MySQL)或其他解析库(如regex正则库,Python自带re模块不需安装)。在动手写代码前,先根据规划列出所有需要的依赖库并安装好。

完成以上步骤后,你就拥有了一个干净的Python爬虫开发环境。接下来可以开始编写和运行爬虫代码了。

3. 基础爬取

在了解了爬虫原理并准备好环境后,我们可以编写第一个简单的爬虫。基础爬取通常涉及两个部分:获取网页内容解析网页内容

3.1 使用Requests获取网页

Python的requests库使HTTP请求变得非常简单。以下是使用requests获取网页HTML内容的基本示例:

import requests

url = "http://example.com"  # 目标网页的URL
headers = {"User-Agent": "Mozilla/5.0"}  # 设置User-Agent头,伪装成浏览器
response = requests.get(url, headers=headers)

print(response.status_code)    # 输出HTTP状态码,200表示成功
print(response.text[:500])     # 输出返回内容的前500个字符

解释: 上述代码向example.com发送一个GET请求,并打印了响应的状态码和部分正文。我们构造了一个简单的headers字典来设置User-Agent,这是为了防止某些网站拒绝响应默认的爬虫请求。response.text属性包含了Unicode解码后的文本内容(HTML源码),而response.content则包含原始的字节流。如果你需要处理图片、PDF等二进制内容,可以使用response.content

在实际编写爬虫时,发送请求需要考虑:

  • 错误处理: 如果status_code不是200,可能需要处理重定向(3xx)或访问错误(4xx/5xx)。可以使用response.status_code判断,或使用requests的异常处理,例如捕获requests.exceptions.RequestException
  • 超时和重试: 使用requests.get(url, timeout=5)设置超时,避免请求卡住。此外可以使用requests的重试机制或第三方库,提高健壮性。
  • Session维持: 使用requests.Session()保持会话,cookies会自动保存和发送,这对需要登录的网站非常有用。

3.2 解析HTML内容

获取到网页HTML后,我们需要从中提取有用的数据。通常有两种常用方法:基于DOM解析(如BeautifulSoup、lxml)和正则表达式。先介绍DOM解析法:

使用BeautifulSoup解析:

from bs4 import BeautifulSoup

html = response.text  # 假设这是我们获取的HTML字符串
soup = BeautifulSoup(html, 'lxml')  # 用lxml解析器,也可以使用默认的html.parser

title = soup.find('title').get_text()        # 找到标签并获取文本
all_links = [a['href'] for a in soup.find_all('a', href=True)]  # 获取页面中所有链接URL
print("页面标题:", title)
print("链接数:", len(all_links))
</code></pre> 
  <p>在这段代码中,我们:</p> 
  <ul> 
   <li>使用<code>BeautifulSoup(html, 'lxml')</code>创建了一个BeautifulSoup对象,这会将HTML字符串解析成一个DOM树结构,方便我们搜索元素。这里指定使用<code>lxml</code>解析器会更快;如果未安装lxml,也可以用内置的<code>html.parser</code>。</li> 
   <li><code>soup.find('title')</code>返回第一个匹配<code><title></code>标签的Tag对象,我们接着用<code>.get_text()</code>获取其中的文本内容。</li> 
   <li><code>soup.find_all('a', href=True)</code>则找到所有带<code>href</code>属性的<code><a></code>链接标签,并用列表推导式提取每个链接的URL。</li> 
  </ul> 
  <p><strong>使用CSS选择器:</strong> BeautifulSoup还支持类似jQuery的CSS选择器语法,通过<code>select()</code>方法:</p> 
  <pre><code># 使用 CSS 选择器提取数据
headings = soup.select("h2.article-title")  # 例如:提取class为article-title的<h2>元素
for h in headings:
    print(h.get_text())
</code></pre> 
  <p>如果熟悉网页前端开发,这种方式会非常直观,比如选择器<code>div.content > ul li</code>可以获取特定层级的元素。</p> 
  <p><strong>使用lxml解析和XPath:</strong> 有时我们也会直接使用<code>lxml</code>的etree模块:</p> 
  <pre><code>from lxml import etree

parser = etree.HTMLParser()
tree   = etree.fromstring(html, parser)
titles = tree.xpath("//title/text()")  # 用XPath获取<title>文本
links  = tree.xpath("//a/@href")       # 获取所有<a>的href属性值
print("页面标题:", titles[0] if titles else "")
print("链接数:", len(links))
</code></pre> 
  <p>这里<code>etree.fromstring</code>结合<code>HTMLParser</code>将HTML解析成可查询的树,<code>.xpath()</code>方法返回符合XPath表达式的所有结果列表。<code>//title/text()</code>表示获取所有<code><title></code>标签下的文本节点,<code>//a/@href</code>则获取所有<code><a></code>标签的href属性。XPath在复杂嵌套的HTML提取中非常强大,我们会在下一节详细介绍。</p> 
  <p><strong>解析结果验证:</strong> 无论使用哪种解析方法,拿到数据后最好打印或检查一下是否符合预期。如果提取不到数据,可能是选择器写得不对,或者目标网页是通过JavaScript动态加载数据(这需要特殊处理,后面会提到模拟浏览器)。</p> 
  <p>通过Requests获取HTML并用BeautifulSoup解析,是大部分Python爬虫的基础流程。在掌握这点后,我们可以进一步学习不同的数据提取技巧和处理更复杂的页面。</p> 
  <h3>4. 数据提取</h3> 
  <p>在上一节中,我们通过DOM方法提取了HTML中的信息。本节将更系统地介绍<strong>数据提取</strong>的几种技术,包括正则表达式、XPath以及处理JSON格式的数据。选择合适的提取方式取决于网页的结构和数据呈现形式:</p> 
  <h4>4.1 正则表达式解析</h4> 
  <p>正则表达式(Regular Expression)是一种强大的字符串匹配工具,对于提取特定格式的文本非常有效。Python内置<code>re</code>模块支持正则操作。</p> 
  <p><strong>使用正则提取示例:</strong> 假设我们想从网页文本中提取所有Email地址,可以使用正则:</p> 
  <pre><code>import re
text = "联系我:email@example.com 或 admin@test.org"
pattern = r'[A-Za-z0-9\._+-]+@[A-Za-z0-9\.-]+\.[A-Za-z]{2,}'  # 匹配Email模式
emails = re.findall(pattern, text)
print(emails)  # 输出: ['email@example.com', 'admin@test.org']
</code></pre> 
  <p>在这个示例中:</p> 
  <ul> 
   <li><code>pattern</code>是Email的正则规则:匹配用户名部分(允许字母数字和._+-),@符号,域名部分,和后面的顶级域(2位以上字母)。</li> 
   <li><code>re.findall()</code> 会返回所有匹配的字符串列表。如果只想找到第一个匹配,可以用 <code>re.search()</code>,如果要替换则用 <code>re.sub()</code>。</li> 
   <li>正则在处理高度自由的纯文本时很有用,比如从网页中提取所有符合某模式的字符串(电话、邮编等)。</li> 
  </ul> 
  <p><strong>注意:</strong> 尽管正则表达式功能强大,但用于解析HTML时需要谨慎。因为HTML是层级结构化的数据,用正则匹配嵌套的标签容易写出复杂难维护的模式,而且稍微变化的结构可能导致匹配失败。一般来说:</p> 
  <ul> 
   <li>对于结构明确、简单的任务,可以使用正则快速提取(例如从一段HTML中抓取所有图片URL:<code><img src="(.*?)"></code>)。</li> 
   <li>更多情况下,**优先使用解析器(BeautifulSoup、lxml)**来处理HTML,把正则留给纯文本提取或辅助清理数据。</li> 
  </ul> 
  <h4>4.2 XPath 解析</h4> 
  <p>XPath是一种在XML/HTML文档中定位节点的语言,适合结构清晰、嵌套层次较深的页面解析。使用XPath可以用简洁的表达式获取节点集,前面已经简单示例过。下面更详细的例子:</p> 
  <pre><code>from lxml import etree

html = """<div class="article"><h1>标题</h1><p>内容段落1</p><p>内容段落2</p></div>"""
tree = etree.HTML(html)  # 直接解析成HTML树
# 提取h1文本
title = tree.xpath("//div[@class='article']/h1/text()")
# 提取p标签文本
paragraphs = tree.xpath("//div[@class='article']/p/text()")
print("标题:", title[0] if title else "")
print("段落:", paragraphs)  # 列出所有段落文本
</code></pre> 
  <p>在这个例子中:</p> 
  <ul> 
   <li><code>"//div[@class='article']/h1/text()"</code> 表示找到<code>class="article"</code>的,在其中直接子节点里选,取其文本。</li> 
   <li><code>"//div[@class='article']/p/text()"</code> 则获取内所有的文本,返回列表。</li> 
   <li>我们用<code>etree.HTML()</code>快捷地把字符串转换为可查询对象,也可以用<code>etree.fromstring</code>类似效果。</li> 
  </ul> 
  <p><strong>更多XPath语法:</strong></p> 
  <ul> 
   <li><code>//tag</code>:选取所有tag,不考虑位置。</li> 
   <li><code>/tag1/tag2</code>:选取tag1下的直接子节点tag2。</li> 
   <li><code>@attr</code>:选取属性值,例如<code>//img/@src</code>拿到所有<code><img></code>的src。</li> 
   <li><code>contains(@attr, 'value')</code>:用于匹配属性中包含特定值的元素,如<code>//div[contains(@class, 'article')]</code>匹配class属性包含'article'的。</li> 
   <li><code>text()</code>:获取元素文本;<code>node()</code>可以获取文本或子元素。</li> 
   <li><code>[index]</code>:选取特定序号的元素(1表示第一个),如<code>(//p)[1]</code>表示文档中第一个。</li> 
  </ul> 
  <p>XPath相比BeautifulSoup的主要优势在于<strong>定位精准</strong>和<strong>支持更复杂的条件查询</strong>。Scrapy框架的Selector就支持XPath和CSS选择器,让我们灵活选用。但初学者可能需要一些时间熟悉XPath的语法。</p> 
  <h4>4.3 JSON 数据解析</h4> 
  <p>现代网站往往会通过后端接口返回JSON格式的数据,或在网页中嵌入JSON。例如,有些网页通过Ajax请求获取数据,直接返回JSON而非HTML。这种情况下,我们可以直接请求该API接口,然后解析JSON。</p> 
  <p><strong>请求返回JSON示例:</strong></p> 
  <pre><code>import requests, json

api_url = "https://api.github.com/repos/python/cpython"
res = requests.get(api_url)
data = res.json()  # 直接将返回内容解析为JSON(字典/列表)
print(f"Repository: {data['name']}, Stars: {data['stargazers_count']}")
</code></pre> 
  <p>在这个例子里,我们调用GitHub的API获取Python语言的仓库信息,<code>res.json()</code>快捷地把返回内容转成Python字典,然后我们就能按键访问各字段。如果不使用<code>res.json()</code>,也可以用<code>json.loads(res.text)</code>达到同样效果。</p> 
  <p><strong>解析嵌入的JSON:</strong> 有时网页的HTML中包含一段JSON,例如:</p> 
  <pre><code><script id="data">{"name": "Alice", "age": 30}</script>
</code></pre> 
  <p>我们可以在爬虫中先用BeautifulSoup找到这个标签的内容,再用<code>json.loads()</code>解析:</p> 
  <pre><code>script_tag = soup.find("script", {"id": "data"})
if script_tag:
    data_text = script_tag.get_text()
    data = json.loads(data_text)
    print(data["name"], data["age"])
</code></pre> 
  <p>这样便能提取页面中嵌入的数据结构。</p> 
  <p><strong>处理JSON列表/字典:</strong> 一旦拿到Python中的列表或字典(通过<code>json.loads</code>或<code>res.json()</code>),后续处理就跟普通数据结构一样了。例如可以遍历列表、按键取值、甚至组合使用pandas来分析。关键是确保成功抓取到正确的JSON文本并转换。</p> 
  <p><strong>总结:</strong> 数据提取手段很多:</p> 
  <ul> 
   <li>简单场景下,正则可以快速拉取需要的字符串。</li> 
   <li>结构化HTML,用BeautifulSoup或lxml进行DOM解析更稳健。</li> 
   <li>动态数据接口,直接请求JSON再解析最高效。</li> 
   <li>根据任务需要,有时这些方法可以混合使用,比如先用DOM解析定位大块内容,再用正则清理细节,或者获取JSON后仍需一些字符串处理。</li> 
  </ul> 
  <p>熟练掌握多种解析方法,可以应对不同的网站结构和数据格式,这是一个合格爬虫工程师的必备技能。</p> 
  <h3>5. 存储数据</h3> 
  <p>将爬取到的数据进行妥善保存是爬虫工作的最后一步。根据用途和数据量的不同,我们可以选择多种存储方式,包括保存为文本文件(CSV/JSON)、关系型数据库、NoSQL数据库等。本节重点介绍几种常用的存储方案及代码示例。</p> 
  <h4>5.1 保存到 CSV 文件</h4> 
  <p>CSV(Comma-Separated Values)是一种简单的文本格式,常用于保存表格数据,每一行是一条记录,各字段以逗号分隔。Python内置<code>csv</code>模块可方便地写入CSV。</p> 
  <p><strong>示例:将爬取的数据列表保存为CSV:</strong></p> 
  <pre><code>import csv

# 假设我们爬取到的数据结构如下 (列表里面每个元素是一个包含数据的字典)
items = [
    {"title": "示例文章1", "author": "张三", "date": "2025-03-01"},
    {"title": "示例文章2", "author": "李四", "date": "2025-03-02"},
]

with open("articles.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    # 写入表头
    writer.writerow(["标题", "作者", "日期"])
    # 写入每一行数据
    for item in items:
        writer.writerow([item["title"], item["author"], item["date"]])
print("CSV文件保存完成")
</code></pre> 
  <p>在上面的代码中:</p> 
  <ul> 
   <li>使用<code>open()</code>打开(或创建)一个名为<code>articles.csv</code>的文件,模式为写入<code>"w"</code>。<code>newline=""</code>参数避免不同操作系统换行符差异导致的空行问题,<code>encoding="utf-8"</code>确保中文写入不乱码。</li> 
   <li>用<code>csv.writer(f)</code>创建写入器,然后先写入表头行,再逐条写入数据行。</li> 
   <li>每条记录以列表形式提供给<code>writer.writerow()</code>,顺序要和表头对应。</li> 
  </ul> 
  <p>这样生成的CSV文件可以用Excel、Google表格等软件打开查看。对于结构规整、以行为单位的数据,CSV是一种直观并通用的存储形式。</p> 
  <p>若我们的数据本身就是字典列表,也可以使用<code>csv.DictWriter</code>按字典键写入,更方便。但无论哪种方式,都需要提前规划好字段顺序和名称。</p> 
  <h4>5.2 保存为 JSON 文件</h4> 
  <p>JSON(JavaScript Object Notation)适合存储结构化且层次分明的数据,例如列表、字典的嵌套。Python的<code>json</code>模块可以将对象轻松序列化为JSON字符串。</p> 
  <p><strong>示例:将数据保存为本地JSON文件:</strong></p> 
  <pre><code>import json

data = {
    "timestamp": "2025-03-04 22:00:00",
    "articles": items  # 复用上面定义的 items 列表
}
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=4)
print("JSON文件保存完成")
</code></pre> 
  <p>这里我们创建了一个包含爬取时间戳和文章列表的字典<code>data</code>:</p> 
  <ul> 
   <li><code>json.dump(obj, f)</code>直接将对象序列化并写入文件。</li> 
   <li><code>ensure_ascii=False</code>参数确保非ASCII字符(如中文)以原始形式输出,而不是<code>\u4e00\u4e8c</code>这样的Unicode转义。</li> 
   <li><code>indent=4</code>让输出有缩进,便于阅读。若追求文件小,可以省略这个参数以紧凑格式写出。</li> 
  </ul> 
  <p>JSON文件在程序之间传递数据非常方便,而且保留了丰富的结构信息。但对非技术人员来说,可读性不如CSV直观,需要用专门的软件查看。</p> 
  <h4>5.3 存储到数据库</h4> 
  <p>当数据量较大,或需要支持复杂查询、长期保存时,将数据存入数据库是更稳健的方案。常用的关系型数据库有SQLite、MySQL/PostgreSQL等,NoSQL有MongoDB等。这里以<strong>SQLite</strong>和<strong>MySQL</strong>为例,介绍基本使用。</p> 
  <p><strong>使用 SQLite 数据库:</strong><br> SQLite是一种轻量级的嵌入式关系数据库,Python内置<code>sqlite3</code>模块,无需额外安装,非常适合小型爬虫项目。SQLite将数据存储在一个<code>.db</code>文件中。</p> 
  <pre><code>import sqlite3

# 连接SQLite数据库(如果文件不存在会自动创建)
conn = sqlite3.connect("data.db")
cursor = conn.cursor()
# 创建表(如果已存在则跳过)
cursor.execute("""
    CREATE TABLE IF NOT EXISTS articles (
        id    INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT,
        author TEXT,
        date  TEXT
    )
""")
# 插入数据
for item in items:
    cursor.execute(
        "INSERT INTO articles (title, author, date) VALUES (?, ?, ?)",
        (item["title"], item["author"], item["date"])
    )
conn.commit()  # 提交事务,确保写入
conn.close()   # 关闭连接
print("数据已插入SQLite数据库")
</code></pre> 
  <p>在这个示例中,我们:</p> 
  <ul> 
   <li>用<code>sqlite3.connect()</code>建立数据库连接,如果指定的数据库文件不存在,SQLite会创建一个同名文件。</li> 
   <li>创建一个<code>articles</code>表,包含id(主键)、标题、作者、日期字段。使用<code>IF NOT EXISTS</code>确保脚本重复运行时不会重复创建表。</li> 
   <li>遍历数据列表,用<code>cursor.execute</code>执行插入SQL语句,<code>?</code>是占位符,后面的tuple提供实际的值。这种参数化可以防止SQL注入问题。</li> 
   <li>最后<code>commit</code>提交事务,并关闭连接。生成的<code>data.db</code>文件可以用SQLite管理工具查看,或者通过代码查询。</li> 
  </ul> 
  <p><strong>使用 MySQL 数据库:</strong><br> MySQL等客户端/服务器式数据库适合更大规模的数据和并发访问。Python可以使用<code>pymysql</code>或<code>MySQLdb</code>驱动连接MySQL。以下是简要的流程(不执行实际连接):</p> 
  <pre><code>import pymysql

# 建立MySQL连接 (请替换实际的主机、用户名、密码、数据库名)
conn = pymysql.connect(host="localhost", user="root", password="123456", database="mydb", charset="utf8mb4")
cursor = conn.cursor()
# 创建表
cursor.execute("""
    CREATE TABLE IF NOT EXISTS articles (
        id INT PRIMARY KEY AUTO_INCREMENT,
        title VARCHAR(255),
        author VARCHAR(100),
        date DATE
    )
""")
# 插入数据
for item in items:
    cursor.execute(
        "INSERT INTO articles (title, author, date) VALUES (%s, %s, %s)",
        (item["title"], item["author"], item["date"])
    )
conn.commit()
conn.close()
</code></pre> 
  <p>这里占位符使用<code>%s</code>,其他逻辑与SQLite类似。使用MySQL时需要提前安装数据库服务器、创建数据库,并提供正确的连接参数。字段类型也相对严格,需要根据数据设计(上例把日期存为DATE类型)。</p> 
  <p><strong>其他存储:</strong></p> 
  <ul> 
   <li>对于文档型数据,<strong>MongoDB</strong>是常用的NoSQL数据库,可以用<code>pymongo</code>库进行插入和查询。</li> 
   <li>如果数据需要进一步分析,<strong>Pandas</strong>库可以将数据存为DataFrame,再直接输出为Excel、或利用其IO接口保存为SQL表等。</li> 
   <li>在Scrapy框架中,提供了Item Pipeline,可以方便地对接数据库或保存文件,只需定义好Pipeline类即可自动处理爬取项的保存。</li> 
  </ul> 
  <p>选择存储方式取决于应用场景:<strong>短期的小数据量</strong>可以用CSV/JSON方便共享,<strong>长期的大数据量</strong>应该考虑数据库便于检索分析。如果只是练习,小规模数据直接打印或存CSV已经足够;但构建真实项目时,设计好数据库 Schema 将使数据更有价值。</p> 
  <h3>6. 高级爬虫:使用Scrapy框架</h3> 
  <p>当爬取需求变得复杂或规模较大时,手工编写requests+BS4脚本可能会变得难以维护。这时可以考虑使用 <strong>Scrapy</strong> —— 一个为爬虫开发打造的高性能框架。Scrapy提供了请求调度、解析、管道、并发等一系列机制,使我们能更快速地构建一个健壮的爬虫。下面我们概览Scrapy的核心概念和流程,并给出简单示例。</p> 
  <p>(Architecture overview — Scrapy 2.12.0 documentation) (Architecture overview — Scrapy 2.12.0 documentation)<strong>Scrapy 架构概览:</strong> 上图展示了Scrapy内部架构和数据流。Scrapy包含几个主要组件:</p> 
  <ul> 
   <li><strong>Engine(引擎):</strong> 核心调度器,负责各组件间的数据流转。引擎按照一定流程推动爬虫进行,比如从Scheduler取出下一个待抓取请求、将响应交给Spider解析等。</li> 
   <li><strong>Scheduler(调度器):</strong> 请求队列管理器。它接受引擎发来的请求(Request)并加入队列,再在引擎需要时吐出下一个请求,实现对待爬取URL的调度管理,可视为一个优先队列(Queues)。</li> 
   <li><strong>Downloader(下载器):</strong> 负责执行HTTP/HTTPS请求,将请求发送到互联网,并获取网页响应(Response)。Downloader在Scrapy中由高效的异步网络框架(Twisted)实现,能够高速并发下载。</li> 
   <li><strong>Spider(爬虫/蜘蛛):</strong> 由用户编写的爬虫解析代码。Spider定义了要爬取的起始URL、如何解析页面从而提取数据(Item)以及新的后续请求。每个Spider通常对应一类网站的抓取逻辑。</li> 
   <li><strong>Item Pipeline(项目管道):</strong> 处理Spider提取出的Item数据的组件。典型管道任务包括清洗数据、去重验证、将Item保存到文件或数据库等。可以有多个Pipeline按顺序执行。</li> 
   <li><strong>Middlewares(中间件):</strong> 包括Downloader Middleware和Spider Middleware,是Scrapy提供的钩子,可以定制请求和响应的处理。例如Downloader Middleware可以拦截请求加上代理或修改Headers,Spider Middleware可以在Item传递前后做额外处理等。</li> 
  </ul> 
  <p>整个Scrapy的工作流程大致如下:Engine从Spider获取初始请求 -> Scheduler安排请求 -> Downloader获取响应 -> Spider解析响应返回Item或新的请求 -> Item交给Pipeline处理,新的请求再给Scheduler,周而复始,直到没有新的请求。Scrapy利用这种架构可以非常高效地进行<strong>异步抓取</strong>,默认情况下同一时间可以并发处理多页内容,大大提高爬取速度。</p> 
  <h4>6.1 Scrapy 项目结构</h4> 
  <p>Scrapy鼓励分模块开发。使用命令<code>scrapy startproject mycrawler</code>可以创建一个新的爬虫项目,包含:</p> 
  <ul> 
   <li><code>scrapy.cfg</code>:项目配置文件。</li> 
   <li><code>mycrawler/</code>:项目的Python模块,包含代码。 
    <ul> 
     <li><code>spiders/</code> 目录:放置Spider代码,每个Spider通常是一个Python类文件。</li> 
     <li><code>items.py</code>:定义Item数据结构(类似ORM模型,可以定义字段)。</li> 
     <li><code>pipelines.py</code>:定义Item Pipeline处理逻辑。</li> 
     <li><code>middlewares.py</code>:定义中间件逻辑。</li> 
     <li><code>settings.py</code>:全局配置,例如并发数、下载延迟、Pipeline启用等。</li> 
    </ul> </li> 
  </ul> 
  <p>Scrapy项目可以包含多个Spider。比如你可能写一个Spider爬取新闻网站A,另一个Spider爬取电商网站B,它们共享同一套Pipeline和配置,但Spider实现不同。</p> 
  <h4>6.2 编写一个Spider</h4> 
  <p>Scrapy中的Spider类继承自<code>scrapy.Spider</code>,需要定义几个关键属性和方法:</p> 
  <ul> 
   <li><code>name</code>:Spider的名称,用于运行时标识。</li> 
   <li><code>start_urls</code>:初始请求URL列表,或者定义<code>start_requests()</code>方法来自行生成初始请求。</li> 
   <li><code>parse</code>方法:默认的解析回调函数,每当有新的响应返回时,Scrapy会调用这个方法,传入<code>response</code>对象。我们在此方法中编写解析逻辑,提取数据和生成新的请求。</li> 
  </ul> 
  <p>以下是一个简单的Spider示例(爬取 Quotes 网站的示例):</p> 
  <pre><code>import scrapy

class QuotesSpider(scrapy.Spider):
    name = "quotes"                              # 爬虫名称
    start_urls = ["http://quotes.toscrape.com/"]  # 初始爬取页面

    def parse(self, response):
        # 提取名言和作者
        for quote in response.css("div.quote"):
            text = quote.css("span.text::text").get()       # 提取名言文本
            author = quote.css("small.author::text").get()  # 提取作者名字
            yield { "text": text, "author": author }        # 产生一个数据项(Item)

        # 找到下一页链接,构造新的请求
        next_page = response.css("li.next a::attr(href)").get()
        if next_page is not None:
            # 使用response.follow保持相对URL正确拼接,并指定解析函数
            yield response.follow(next_page, callback=self.parse)
</code></pre> 
  <p><strong>解释:</strong></p> 
  <ul> 
   <li>这个Spider名为quotes,起始页面是quotes.toscrape.com的首页。</li> 
   <li><code>response.css("div.quote")</code>使用了CSS选择器获取页面中所有类名为quote的<code><div></code>元素(每个包含一条名言)。我们在循环中对每个quote块,用子选择器提取文本和作者,并用<code>yield</code>返回一个字典。Scrapy会自动把这个字典当作Item处理。</li> 
   <li>然后检查是否存在下一页链接(页面底部有“Next”按钮),如果存在就构造一个新的请求。<code>response.follow</code>是Scrapy提供的简便方法,它会自动处理相对路径拼接为绝对URL,并让我们指定由哪个回调方法处理响应(这里继续使用同一个<code>parse</code>方法解析下一页)。</li> 
   <li>就这样,Scrapy会不断深度爬取这个网站的分页直到没有下一页。所有yield出的Item会进入Item Pipeline处理,或者根据设置存储为JSON/CSV等格式。</li> 
  </ul> 
  <h4>6.3 Scrapy中间件与Pipeline</h4> 
  <p><strong>Downloader Middleware(下载中间件):</strong> Scrapy允许在请求发出前、响应返回后插入自定义处理逻辑。例如我们可以编写Downloader Middleware来随机更换User-Agent、设置代理IP、或处理重定向等。Middleware本质上是一些钩子函数,有点像洋葱圈模型,Request出去和Response回来都会经过中间件链。启用中间件需要在<code>settings.py</code>中配置。</p> 
  <p><strong>Spider Middleware:</strong> 主要作用于Spider处理环节,可以在Spider解析前后做处理,使用相对较少,一般默认配置足矣。</p> 
  <p><strong>Item Pipeline:</strong> 前面提到,Pipeline负责处理Item的数据结果。比如我们想把上面QuotesSpider抓取到的名言保存到数据库,就可以在<code>pipelines.py</code>中创建一个类:</p> 
  <pre><code>class SaveQuotesPipeline:
    def open_spider(self, spider):
        self.conn = sqlite3.connect("quotes.db")
        self.cursor = self.conn.cursor()
        self.cursor.execute("""CREATE TABLE IF NOT EXISTS quotes (text TEXT, author TEXT)""")

    def process_item(self, item, spider):
        self.cursor.execute("INSERT INTO quotes VALUES (?, ?)", (item['text'], item['author']))
        return item  # 返回item以便下一个Pipeline(如果有)处理

    def close_spider(self, spider):
        self.conn.commit()
        self.conn.close()
</code></pre> 
  <p>并在<code>settings.py</code>中启用:</p> 
  <pre><code>ITEM_PIPELINES = {
    'mycrawler.pipelines.SaveQuotesPipeline': 300,
}
</code></pre> 
  <p>其中数字<code>300</code>表示Pipeline的执行顺序优先级(越小越先执行)。</p> 
  <p>Scrapy还有很多强大之处,如自动的请求去重、异常重试、日志和调试工具(<code>scrapy shell</code>交互调试特定页面)等等。尽管学习曲线相对爬虫脚本要高一些,但Scrapy对于复杂爬虫项目的开发维护无疑更加高效。</p> 
  <p><em>提示:如果你是初学者,在掌握Requests+BS4写简单爬虫后,再学习Scrapy会比较顺畅,因为Scrapy的很多概念(Request、Response、解析)与基础方法类似,只是框架帮你做了调度和优化。</em></p> 
  <h3>7. 反反爬策略</h3> 
  <p>当我们爬取网站时,常常会遇到网站的<strong>反爬虫机制</strong>:如要求登录、使用验证码、限制请求频率、检查请求头等。为提高爬虫的成功率,我们需要采取一些“反反爬”策略,模拟更接近真实用户的行为,避开被网站检测到是爬虫。以下是常见的反爬对抗技巧:</p> 
  <ul> 
   <li> <p><strong>伪装 User-Agent:</strong> 大部分爬虫库(如requests)默认的User-Agent字符串明显带有<code>Python-requests</code>字样,容易被网站屏蔽。我们可以将请求头中的User-Agent更改为常见浏览器标识。例如:</p> <pre><code>headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)... Safari/537.36"
}
requests.get(url, headers=headers)
</code></pre> <p>也可以使用第三方库如<code>fake-useragent</code>随机生成不同浏览器的UA字符串,每次请求更换,增加多样性。</p> </li> 
   <li> <p><strong>使用 Cookies 和 会话:</strong> 有些网站需要维护登录状态或者根据上一次访问情况返回内容。利用<code>requests.Session</code>对象可以自动保存cookie,下次请求时带上。或者手动从浏览器复制登录后的cookies,在请求时附加:</p> <pre><code>cookies = {"sessionid": "abcd1234..."}  # 伪造登录后的cookie
requests.get(url, headers=headers, cookies=cookies)
</code></pre> <p>这样服务器会认为是已登录用户,提高访问权限。注意滥用他人Cookies是违法的,这里仅讨论技术手段。</p> </li> 
   <li> <p><strong>设置请求频率和随机延迟:</strong> 快速连续的请求容易触发反爬。可以在爬虫中加入<code>time.sleep()</code>随机睡眠几秒再请求下一个页面,或在Scrapy中设置DOWNLOAD_DELAY等参数。通过降低速度、模拟人类浏览节奏来降低被封的概率。另外,可以对爬取顺序进行随机打乱,不要每次都严格按照固定间隔抓取同一模式。</p> </li> 
   <li> <p><strong>使用代理IP:</strong> 如果爬取频率较高或目标网站对单IP请求数有限制,可以使用代理服务器来切换IP地址。网上有免费代理IP,但质量参差不齐,更可靠的是购买付费代理服务。使用requests设置HTTP代理:</p> <pre><code>proxies = {"http": "http://123.123.123.123:8080", "https": "https://123.123.123.123:8080"}
requests.get(url, headers=headers, proxies=proxies)
</code></pre> <p>Scrapy中也可以在settings中配置<code>HTTP_PROXY</code>或使用scrapy-proxies中间件。代理池要定期更换检测,确保IP未被目标网站封禁。</p> </li> 
   <li> <p><strong>动态模拟 Headers:</strong> 除了User-Agent,还可以模仿浏览器发送的其他Headers,例如<code>Accept-Language</code>(接受语言),<code>Referer</code>(引荐页面),<code>Accept-Encoding</code>(接受的压缩格式)等。合理设置这些头信息可以让请求更“正常”。最简单办法是从浏览器开发者工具复制真实请求的所有Headers,在代码里使用同样的。</p> </li> 
   <li> <p><strong>模拟浏览器行为:</strong> 某些网站通过执行复杂的JavaScript生成内容或检查行为(如鼠标移动、点击)。对此最有效的办法是采用<strong>Selenium</strong>等浏览器自动化工具,直接驱动真实浏览器完成操作,然后获取渲染后的页面源代码。示例:</p> <pre><code>from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.headless = True  # 使用无头模式,不打开浏览器窗口
driver = webdriver.Chrome(options=options)
driver.get("https://example.com/")   # 让Chrome打开页面
content = driver.page_source        # 获取动态渲染后的HTML
driver.quit()
</code></pre> <p>Selenium支持与页面元素交互,如点击、表单填写等,能突破很多反爬限制。不过它的缺点是<strong>速度慢</strong>、<strong>资源占用高</strong>,不适合大规模爬取。可以配合无头浏览器(如Chrome Headless、PhantomJS)或者无界面模拟(如Splash服务器)来加快一定速度,但相较于Requests还是慢很多。</p> </li> 
   <li> <p><strong>识别并绕过验证码:</strong> 当遇到验证码(常见于登录或频繁访问后弹出),基本宣告自动化受阻。常见策略包括:人工打码(将验证码图片截图保存,让人工或打码平台识别填写)、使用OCR技术自动识别简单验证码,或者分析网站有没有提供验证码绕过的机制。但这些都超出了一般爬虫的范畴,而且处理不当可能违法。通常个人爬虫遇到复杂验证码往往只能放弃或减小频率避免触发。</p> </li> 
   <li> <p><strong>混淆爬虫特征:</strong> 有的网站可能通过检测请求的某些特征来识别爬虫,比如请求顺序、参数特征、IP地址地理位置等。对此可以做的是:<strong>随机化</strong>——包括访问顺序随机、使用不同账号或不同IP地域去请求等,尽量避免呈现固定模式。另外,使用分布式爬虫框架和多个节点从不同地方爬,也是一种办法(如Scrapy结合scrapy-redis组件实现分布式调度)。</p> </li> 
  </ul> 
  <p><strong>小结:</strong> 反反爬是一场与网站策略的博弈。以上方法可能需要组合使用。例如爬取某网站可能同时需要模拟登录(cookies)、降低频率、使用代理、更换UA等才能顺利获取数据。要根据具体的反爬手段进行针对性应对。同时也要把握分寸,<strong>尊重网站的反爬措施</strong>。如果对方明确不希望被爬取或已强力防护,那么贸然绕过不但花费巨大代价,也可能惹上法律麻烦。</p> 
  <h3>8. 实战案例:爬取示例网站</h3> 
  <p>理论讲解之后,我们通过一个完整的示例来演练如何从零开始实现一个爬虫脚本。<strong>目标:</strong> 爬取一个示例网站的多页内容,并将数据保存到文件。本案例选用Quotes to Scrape网站,这是一个专门供爬虫练习的公开站点,页面上有名人名言及作者,并支持分页,非常适合作为示范。</p> 
  <p><strong>任务描述:</strong> 抓取该网站上的所有名言文本和作者名,并将结果保存到CSV文件中。</p> 
  <h4>8.1 分析目标网站</h4> 
  <p>用浏览器打开目标网站首页,可以看到每条名言的HTML结构(可以按F12打开开发者工具查看DOM):</p> 
  <pre><code><div class="quote">
    <span class="text">“The world as we have created it is a process of our thinking. ...”</span>
    <small class="author">Albert Einstein</small>
    <div class="tags">
        Tags: <a class="tag" href="...">change</a> <a class="tag" href="...">deep-thoughts</a> ...
    </div>
</div>
</code></pre> 
  <p>每个.quote包含一句话<code>.text</code>、作者<code>.author</code>和若干标签,我们需要提取前两项。底部分页部分(仅展示结构):</p> 
  <pre><code><ul class="pager">
    <li class="next"><a href="/page/2/">Next <span aria-hidden="true">→</span></a></li>
</ul>
</code></pre> 
  <p>可以看到“Next”按钮的链接在<code><li class="next"></code>中,我们可以据此找到下一页的URL。</p> 
  <h4>8.2 编写爬虫代码</h4> 
  <p>我们将使用requests和BeautifulSoup来实现,逻辑为:从第一页开始抓 -> 解析数据 -> 找到下一页链接 -> 循环抓取直至没有下一页。以下是完整代码:</p> 
  <pre><code>import requests
from bs4 import BeautifulSoup
import csv

base_url = "http://quotes.toscrape.com"  # 基础URL
start_url = base_url + "/"              # 起始页

all_quotes = []  # 用于存储抓取到的所有名言和作者

url = start_url
while url:
    # 获取页面内容
    res = requests.get(url)
    soup = BeautifulSoup(res.text, 'lxml')
    # 解析名言块
    quote_divs = soup.find_all("div", class_="quote")
    for div in quote_divs:
        text = div.find("span", class_="text").get_text(strip=True)   # 名言文本
        author = div.find("small", class_="author").get_text(strip=True)  # 作者
        all_quotes.append((text, author))
    # 查找下一页链接
    next_btn = soup.find("li", class_="next")
    if next_btn:
        next_url = next_btn.find("a")["href"]   # 如 "/page/2/"
        url = base_url + next_url              # 拼接绝对URL
    else:
        url = None  # 没有下一页,结束循环

# 将结果保存到CSV文件
with open("quotes.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.writer(f)
    writer.writerow(["Quote", "Author"])  # 写入表头
    writer.writerows(all_quotes)
print(f"抓取完成,共保存 {len(all_quotes)} 条名言到 quotes.csv")
</code></pre> 
  <p><strong>代码说明:</strong></p> 
  <ul> 
   <li>使用<code>while url:</code>循环,当存在下一页时继续抓取。初始的<code>url</code>为首页地址,循环内每次更新<code>url</code>为下一页。</li> 
   <li>对于每个页面,用<code>requests.get</code>获取HTML文本,然后用<code>BeautifulSoup</code>解析。</li> 
   <li><code>soup.find_all("div", class_="quote")</code> 找出当页的所有名言块。然后对每个块提取文字和作者。其中<code>get_text(strip=True)</code>用于获取纯文本并去除首尾空白。</li> 
   <li>解析完当前页后,通过查找<code><li class="next"></code>判断是否有下一页。如果有,提取里面<code><a></code>标签的href,并拼接成完整URL赋给<code>url</code>。如果没有(最后一页没有.next),则置<code>url=None</code>跳出循环。</li> 
   <li>最后将收集的<code>all_quotes</code>列表写入CSV,每行两列,分别是名言和作者。完成后打印一条总结信息。</li> 
  </ul> 
  <h4>8.3 运行结果</h4> 
  <p>运行上述脚本,程序将逐页爬取该网站的所有10页内容。最终<code>quotes.csv</code>文件将包含所有名言和对应的作者。例如前几行内容如下(这里展示CSV文本,每条记录换行显示):</p> 
  <pre><code>"Quote","Author"
"“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”","Albert Einstein"
"“... A day without sunshine is like, you know, night.”","Steve Martin"
...
</code></pre> 
  <p>我们成功地将目标网站的数据采集下来。这一案例规模不大,但涵盖了<strong>发送请求、解析网页、处理分页、数据存储</strong>等关键步骤。在实际应用中,可以根据类似思路扩展:比如增加异常处理(网络错误重试)、更加复杂的信息提取逻辑、或者将结果存入数据库而非CSV等。</p> 
  <p><strong>提示:</strong> 当你调试爬虫时,建议先限制页数或数据量,确认抓取和解析逻辑正确,再放开爬取所有内容。比如在上述例子中,可先尝试抓取前2页看看输出是否预期,以避免逻辑错误导致爬虫长时间运行却抓取不到想要的数据。</p> 
  <h3>9. 优化与部署</h3> 
  <p>编写完爬虫脚本并不意味着大功告成,实际应用中我们经常需要考虑<strong>如何更快更稳健地爬取</strong>以及<strong>如何在服务器上长期运行</strong>等问题。本节讨论一些爬虫优化技巧和部署方法。</p> 
  <h4>9.1 爬虫性能优化</h4> 
  <p><strong>(1) 并发与异步:</strong><br> 串行的爬虫在遇到大量页面时会非常缓慢,因为需要等待每个请求完成再进行下一个。通过并发手段可以极大提高效率:</p> 
  <ul> 
   <li><em>多线程</em>:使用Python的<code>threading</code>或<code>concurrent.futures.ThreadPoolExecutor</code>,让多个线程同时发请求解析页面。在I/O密集型任务(如网络请求)中,多线程能够提升速度。需要注意线程安全(如对全局数据写入时要加锁)。</li> 
   <li><em>多进程</em>:使用<code>multiprocessing</code>或<code>ProcessPoolExecutor</code>,可以绕过GIL限制,让多个进程各自爬取不同数据。多进程适合CPU密集任务或者需要隔离GI锁的场景,但启动开销较大,数据需跨进程传递,通常用于更重型的并发任务。</li> 
   <li><em>异步IO</em>:利用<code>asyncio</code>框架和<code>aiohttp</code>库,编写异步爬虫。在单线程下实现高并发请求,性能非常出色。示例: <pre><code>import asyncio, aiohttp
async def fetch(session, url):
    async with session.get(url) as resp:
        return await resp.text()

async def main():
    urls = ["http://example.com/page1", "..."]  # 一堆URL
    async with aiohttp.ClientSession() as session:
        tasks = [asyncio.create_task(fetch(session, u)) for u in urls]
        pages = await asyncio.gather(*tasks)
        print("Fetched", len(pages), "pages")

asyncio.run(main())
</code></pre> 这段代码展示了aiohttp的基本用法。在Scrapy内部,其实也是基于异步IO实现高并发的,所以使用Scrapy往往不需要手动处理线程/async,它会帮你并发抓取。</li> 
  </ul> 
  <p><strong>(2) 控制抓取速度:</strong><br> 一味追求速度可能导致被封禁或自身资源耗尽。优化也包含<strong>适当的延迟</strong>和<strong>限速</strong>。Scrapy提供了<code>AutoThrottle</code>扩展,可根据延迟动态调整抓取速度。对于requests自建的爬虫,可以手工控制每秒请求数或在代码中加入sleep。确保既快又稳才是有效的优化。</p> 
  <p><strong>(3) 减少重复请求:</strong><br> 利用缓存或记录机制避免抓取相同页面多次。例如对列表页经常会反复抓取相同的详情页链接,可在请求前检查是否已抓过。Scrapy默认有去重(Scheduler会过滤相同请求),在自写爬虫中可以用集合或数据库记录已访问URL。</p> 
  <p><strong>(4) 提高解析效率:</strong><br> 如果页面很大或者解析逻辑复杂,解析也会耗时。选择高效的解析方式很重要。一般lxml的速度比BeautifulSoup快不少。或者在能用简单字符串操作时就不必构建DOM。此外,尽量精准地提取所需部分,而不是无谓地处理整页数据。</p> 
  <p><strong>(5) 合理利用硬件:</strong><br> 爬虫运行时要关注CPU、内存、网络等资源。多线程可能受制于GIL无法利用多核,适当可以开多进程或分布式部署。内存方面,如果抓了几百万条数据,别一次性全部存在Python列表里,可以边抓边写硬盘(流式处理),避免内存爆炸。对于超大规模爬取,可以考虑使用分布式爬虫框架(如Scrapy+Redis,Heritrix等)和更强劲的硬件。</p> 
  <h4>9.2 部署爬虫</h4> 
  <p>当爬虫脚本需要长时间运行或定期执行时,通常会部署到服务器环境。例如你的爬虫每天抓取一次新闻网站,需要一个稳定的地方每天运行。</p> 
  <p><strong>(1) 本地部署 vs 云服务器:</strong><br> 小型任务可以在自己电脑上用定时任务跑,但要求长时间开机。更可靠的是将爬虫部署到云服务器(如AWS EC2、阿里云ECS等),在Linux环境下后台运行。选择服务器时,根据爬虫的资源需求选配适当的CPU、内存、带宽。</p> 
  <p><strong>(2) 使用任务调度:</strong><br> 在Linux服务器上,可以使用<code>cron</code>定时执行爬虫脚本。例如,编辑<code>crontab -e</code>加入一行:</p> 
  <pre><code>0 2 * * * /usr/bin/python3 /path/to/spider.py >> /path/to/spider.log 2>&1
</code></pre> 
  <p>表示每天凌晨2点运行spider.py并将日志追加输出。Windows下可以用任务计划程序完成类似功能。如果爬虫需要24/7连续运行,可以用shell脚本监控,异常退出时自动重启,或者借助<code>supervisor</code>等进程管理工具来保持运行。</p> 
  <p><strong>(3) 日志与监控:</strong><br> 部署后最好加上日志记录。可以使用Python自带的<code>logging</code>模块,将重要的事件(开始/结束、错误、数据统计)写入日志文件,以便出问题时诊断。Scrapy框架天生带日志配置,自己写的脚本也应至少捕获异常写日志。监控方面,可以简单通过日志检查进度,或更高级地用监控工具对运行的进程、网络IO等进行观察。</p> 
  <p><strong>(4) 容器化部署:</strong><br> 将爬虫打包进Docker容器也是流行的做法。编写一个Dockerfile,把Python环境和依赖装好,复制爬虫代码进去,设置CMD运行。然后在任意主机上只要有Docker,运行该镜像就可启动爬虫。这对部署多个爬虫、迁移环境来说非常方便,也利于将环境配置写定(Infrastructure as Code)。</p> 
  <p><strong>(5) Scrapy部署工具:</strong><br> 如果使用Scrapy,可以利用其提供的<strong>Scrapyd</strong>服务部署。Scrapyd可以在服务器上运行,接收打包好的爬虫项目(.egg或.zip)并提供API启动/停止爬虫。更简单的,Scrapy官方还提供了<strong>Scrapy Cloud</strong>(即Zyte,收费服务),无需自己管服务器即可托管爬虫,并有友好的监控界面。</p> 
  <p><strong>(6) 多机爬取和负载均衡:</strong><br> 对于超大规模的爬取任务,单台机器可能不够,可以考虑多机分担。常见方式是<strong>分布式爬虫</strong>:将URL任务队列放在公共的地方(例如Redis队列),多台爬虫机器同时从队列取URL抓取,抓完的新URL再放回队列,实现分工协作。Scrapy有开源的scrapy-redis组件能比较容易地实现这种调度。部署上则需要一个调度机 + 多个爬虫工作机,并行工作,大幅扩展爬取量。</p> 
  <p>无论哪种部署方案,都要确保<strong>稳定性</strong>和<strong>可持续运行</strong>。有时网络波动、目标网站临时关闭、服务器自身重启等都会中断爬虫,需要有应对措施(如定时检查进程存活、失败后告警等)。</p> 
  <p>部署和运维往往是爬虫项目中容易被忽视但非常重要的一环。一个写得再好的爬虫,如果不能长时间可靠地运行,也难以产生实际价值。</p> 
  <h3>10. 常见问题与技巧</h3> 
  <p>在实际开发和运行爬虫的过程中,你可能会遇到各种各样的问题。最后,我们总结一些常见问题及处理方法、调试技巧和效率提升的要点:</p> 
  <ul> 
   <li><strong>IP被封/请求被拒绝:</strong> 这通常表现为连续收到HTTP 403 Forbidden或请求没有响应。处理办法包括使用代理IP、降低抓取频率、模拟Headers等(参考第7节的反反爬策略)。如果是IP被临时封禁,可暂停爬虫等待一段时间再试。</li> 
   <li><strong>遇到验证码或登陆墙:</strong> 如果目标网站要求登录才能访问数据,尝试使用爬虫提交登录表单或者使用已有账号的Cookies。验证码是更难的障碍,可考虑绕过(有时验证码在首次请求或频繁后才出现,可以分散请求避免触发),或者利用打码服务平台**(付费,人机结合识别)**解决。但需权衡成本和收益。</li> 
   <li><strong>数据提取不全或解析失败:</strong> 可能是因为网页采用了动态加载,爬虫获取的HTML不包含目标信息。这时需要分析网络请求,在浏览器开发者工具的“Network”面板寻找是否有XHR请求获取数据,如果有直接调用这些API接口;若数据通过JS计算得出,则可能需要用Selenium加载后再抓取。调试方法:对照浏览器页面源代码和爬虫获取的源码,找差异。</li> 
   <li><strong>乱码与编码问题:</strong> 有时抓下来的内容是乱码,通常是编码处理不当导致。requests会根据HTTP头猜测编码并赋给<code>response.encoding</code>,但并不总是正确。可以手动设置 <code>response.encoding = 'utf-8'</code> 或其他编码,然后再<code>response.text</code>。对于中文网站,常见编码包括UTF-8和GBK/GB2312。如果乱码,可以尝试用<code>res.content.decode('gbk', errors='ignore')</code>之类的方法解码。BeautifulSoup解析时也要确保给定正确的编码。</li> 
   <li><strong>高并发爬虫的稳定性:</strong> 当使用多线程/异步时,常会遇到一些线程安全或连接超时等问题。例如使用aiohttp需要谨慎处理<code>session</code>的重用和关闭;多线程下如需写入统一文件,要用锁串行化写操作。调试这类问题可以先在小规模并发下运行,逐渐增加线程数观察是否正常,并捕获异常做日志。</li> 
   <li><strong>调试技巧:</strong> 对于抓取和解析阶段的问题,可以使用<strong>交互式调试</strong>。如Scrapy有<code>scrapy shell 'URL'</code>可以打开一个终端调试特定URL的解析,非常好用。自己写脚本时,也可以在报错处打印相关HTML片段或保存到文件手动检查。另外,善用日志打印关键节点信息,能帮助追踪爬虫进度和发现异常。</li> 
   <li><strong>规避触发监控:</strong> 一些大型网站可能对访问行为进行机器学习监控,检测异常模式。应避免过于规则化的行为,比如每次都整点启动,每页精确延迟5秒等。这些都可能成为特征。可以加入一些抖动和随机元素,使爬虫行为更类似真实用户(当然也不能太随机无序而降低效率)。</li> 
   <li><strong>提升爬取效率的小窍门:</strong> 
    <ul> 
     <li>利用<strong>分层爬取</strong>:先抓取索引页列表,再抓取详情页内容,这样可以先拿到整体结构,再并发抓详情。</li> 
     <li>对于特别大的页面,考虑<strong>流式处理</strong>:一边下载一边解析(Python <code>requests</code>可用<code>stream=True</code>获取流),或者先下载到本地文件再逐行解析,减少一次性内存占用。</li> 
     <li>如果频繁爬取同一网站,可以<strong>缓存</strong>一些不变的内容,比如列表页每天可能变化不大,就不要每次都全抓新的,或者用ETag/Last-Modified头和增量抓取的方法,节省流量和时间。</li> 
    </ul> </li> 
   <li><strong>法律和版权</strong>:再次提醒,<strong>合规</strong>运行爬虫很重要。近期一些国家加强了对数据抓取的监管,例如欧盟GDPR对个人数据抓取有严格限制,中国也有《数据安全法》《个人信息保护法》等。不遵守法律可能导致严重后果。在发布或分享抓取的数据前,也要考虑版权和隐私影响,通常公开的统计数据或匿名化的数据风险较小,而涉及个人的信息要慎之又慎。</li> 
  </ul> 
  <p>开发一个爬虫不仅仅是把数据抓下来那么简单,还包括<strong>让它稳定、高效、安全地跑下去</strong>。希望这个教程提供的知识能帮助你构建出成功的Python爬虫项目。</p> 
 </div> 
</div>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1897302699662176256"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(python,爬虫,开发语言,青少年编程,visual,studio,code,github,html5)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1940718619537240064.htm"
                           title="Effective Go 编程技巧总结" target="_blank">Effective Go 编程技巧总结</a>
                        <span class="text-muted">强哥之神</span>
<a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/GPU%E8%B0%83%E5%BA%A6/1.htm">GPU调度</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B/1.htm">语言模型</a><a class="tag" taget="_blank" href="/search/%E4%BA%91%E8%AE%A1%E7%AE%97/1.htm">云计算</a>
                        <div>Go是一种新兴的编程语言。尽管它借鉴了其他语言的许多特性,但也具备一些独特的属性,使得用Go编写的高效程序在风格上与其他语言编写的程序有所不同。直接将C++或Java程序翻译成Go代码,通常无法取得令人满意的结果——Java程序的编写方式是Java风格,而非Go风格。另一方面,如果从Go的语言特性出发去思考问题,可能会编写出风格截然不同但更为成功的程序。换句话说,要编写出优秀的Go代码,理解Go语</div>
                    </li>
                    <li><a href="/article/1940717485959475200.htm"
                           title="探索《非官方知乎 API》:解锁知乎数据潜能指南" target="_blank">探索《非官方知乎 API》:解锁知乎数据潜能指南</a>
                        <span class="text-muted"></span>

                        <div>探索《非官方知乎API》:解锁知乎数据潜能指南Unofficial-Zhihu-API深度学习模型自动识别验证码,python爬虫库自动管理会话,通过简单易用的API,实现知乎数据的爬取项目地址:https://gitcode.com/gh_mirrors/un/Unofficial-Zhihu-API项目介绍非官方知乎API是一个由社区贡献的开源工具,位于https://github.com/l</div>
                    </li>
                    <li><a href="/article/1940716856235061248.htm"
                           title="探索《非官方知乎 API》:解锁知乎数据的新方式" target="_blank">探索《非官方知乎 API》:解锁知乎数据的新方式</a>
                        <span class="text-muted"></span>

                        <div>探索《非官方知乎API》:解锁知乎数据的新方式项目地址:https://gitcode.com/gh_mirrors/un/Unofficial-Zhihu-API在数据分析和社交媒体研究的世界里,拥有可靠的API是至关重要的。今天,我们将深入探讨一个由社区贡献的开源项目——。该项目提供了一个独特的方式,让你能够访问和解析知乎平台上的数据。项目简介非官方知乎API是由开发者littlepai创建的</div>
                    </li>
                    <li><a href="/article/1940715974588166144.htm"
                           title="Linux编程——Makefile 使用" target="_blank">Linux编程——Makefile 使用</a>
                        <span class="text-muted"></span>

                        <div>在先前的文章中,我们已经学习了gcc和gdb的使用。本节,我们将介绍Makefile的使用。Makefile带来的好处就是——“自动化编译”,一但写好,只需要一个make命令,整个工程便可以完全编译,极大的提高了软件的开发效率(特别是对于那些项目较大、文件较多的工程)。make是一个命令工具,最主要也是最基本的功能就是根据makefile文件中描述的源程序至今的相互关系来完成自动编译、维护多个源文</div>
                    </li>
                    <li><a href="/article/1940714714069790720.htm"
                           title="【ARM】解决ArmDS的工程没有生成Map文件的问题" target="_blank">【ARM】解决ArmDS的工程没有生成Map文件的问题</a>
                        <span class="text-muted">亿道电子Emdoor</span>
<a class="tag" taget="_blank" href="/search/ARM/1.htm">ARM</a><a class="tag" taget="_blank" href="/search/ARM/1.htm">ARM</a><a class="tag" taget="_blank" href="/search/arm%E5%BC%80%E5%8F%91/1.htm">arm开发</a>
                        <div>1、文档目标在嵌入式开发过程中,使用ArmDevelopmentStudio(简称ArmDS)进行项目构建时,Map文件的生成是调试和分析代码的重要环节。Map文件不仅记录了程序中各个段(sections)的内存分布情况,还提供了符号地址、函数调用关系等关键信息,对优化代码性能、排查问题具有不可替代的作用。然而,在某些情况下,开发者可能会发现ArmDS工程并未如预期生成Map文件。这一问题可能源于</div>
                    </li>
                    <li><a href="/article/1940713706006573056.htm"
                           title="【Python爬虫实战】全面抓取网页资源(图片、JS、CSS等)——超详细教程与源码解析" target="_blank">【Python爬虫实战】全面抓取网页资源(图片、JS、CSS等)——超详细教程与源码解析</a>
                        <span class="text-muted">Python爬虫项目</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E6%96%B0%E6%B5%AA%E5%BE%AE%E5%8D%9A/1.htm">新浪微博</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E6%97%85%E6%B8%B8/1.htm">旅游</a>
                        <div>前言在互联网时代,网页数据已经成为重要的信息来源。许多时候,我们不仅需要抓取网页中的文字信息,还需要将网页中的各种资源文件(如图片、CSS样式表、JavaScript脚本文件等)一起抓取并保存下来。这种需求广泛应用于网页备份、离线浏览、数据分析等场景。本篇文章将带你从零开始,系统讲解如何使用Python最新技术,一步步实现抓取网页中所有静态资源的完整流程,包括:页面结构分析爬虫基本架构搭建异步爬取</div>
                    </li>
                    <li><a href="/article/1940713578843664384.htm"
                           title="用Python爬虫抓取网页中的视频文件:从数据获取到处理与保存的完整教程" target="_blank">用Python爬虫抓取网页中的视频文件:从数据获取到处理与保存的完整教程</a>
                        <span class="text-muted">Python爬虫项目</span>
<a class="tag" taget="_blank" href="/search/2025%E5%B9%B4%E7%88%AC%E8%99%AB%E5%AE%9E%E6%88%98%E9%A1%B9%E7%9B%AE/1.htm">2025年爬虫实战项目</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/selenium/1.htm">selenium</a>
                        <div>一、引言随着在线视频平台的快速发展,视频成为了互联网中最重要的媒介之一。无论是用于娱乐、教育还是技术学习,视频内容都极大地改变了我们的信息获取方式。对于开发者、数据分析师或者研究者而言,获取和分析视频文件的数据不仅可以帮助他们深入理解某些平台的运营模式,也有助于建立自定义的多媒体内容库。爬虫技术是自动化抓取网页数据的一种工具。它通过模拟浏览器行为,抓取目标网页的内容。对于视频文件的抓取,尤其是那些</div>
                    </li>
                    <li><a href="/article/1940713579607027712.htm"
                           title="使用Python爬虫抓取免费音乐下载网站:从数据抓取到下载" target="_blank">使用Python爬虫抓取免费音乐下载网站:从数据抓取到下载</a>
                        <span class="text-muted">Python爬虫项目</span>
<a class="tag" taget="_blank" href="/search/2025%E5%B9%B4%E7%88%AC%E8%99%AB%E5%AE%9E%E6%88%98%E9%A1%B9%E7%9B%AE/1.htm">2025年爬虫实战项目</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>目录:前言爬虫基础知识什么是Web爬虫爬虫的工作原理抓取音乐下载网站的目标目标网站分析确定抓取数据的元素爬虫技术栈介绍Python爬虫的常用库requests库BeautifulSoup库Selenium库aiohttp和异步抓取抓取音乐下载网站的步骤选择目标网站并分析页面结构使用requests获取网页内容使用BeautifulSoup解析HTML解析音频文件下载链接使用Selenium抓取动态</div>
                    </li>
                    <li><a href="/article/1940712949156999168.htm"
                           title="linux系统编程——Makefile、GDB调试" target="_blank">linux系统编程——Makefile、GDB调试</a>
                        <span class="text-muted">舒克起飞了</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a>
                        <div>Makefilemakefile定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile文件就像一个shell脚本一样,也可以执行操作系统的命令。自动化编译,只要一个make命令,整个工程完全自动编译。规则一个makefile文件中可以有一个或者多个规则目标...:依赖...命令(shell命令)...目标:最终要生成的</div>
                    </li>
                    <li><a href="/article/1940712569136279552.htm"
                           title="西南交通大学【机器学习实验1】" target="_blank">西南交通大学【机器学习实验1】</a>
                        <span class="text-muted"></span>

                        <div>实验目的理解和掌握回归问题和分类问题模型评估方法,学会使用均方误差、最大绝对误差、均方根误差指标评估回归模型,学会使用错误率、精度、查全率、查准率、F1指标评价分类模型。实验内容给定回归问题的真实标签和多个算法的预测结果,编程实现MSE、MAE、RMSE三种评测指标,对模型进行对比分析。给定二分类问题真实标签和多个算法的预测结果,编程实现混淆矩阵评测,采用错误率、精度、查全率、查准率、F1指标对结</div>
                    </li>
                    <li><a href="/article/1940711684540788736.htm"
                           title="WebRTC入门与提高2:WebRTC开发环境" target="_blank">WebRTC入门与提高2:WebRTC开发环境</a>
                        <span class="text-muted">音视频开发老马</span>
<a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91%E5%BC%80%E5%8F%91/1.htm">音视频开发</a><a class="tag" taget="_blank" href="/search/%E6%B5%81%E5%AA%92%E4%BD%93%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">流媒体服务器</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/%E5%AE%9E%E6%97%B6%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">实时音视频</a><a class="tag" taget="_blank" href="/search/%E8%A7%86%E9%A2%91%E7%BC%96%E8%A7%A3%E7%A0%81/1.htm">视频编解码</a><a class="tag" taget="_blank" href="/search/webrtc/1.htm">webrtc</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a>
                        <div>2.1安装vscode下载和安装vscodevscode官网:VisualStudioCode-CodeEditing.Redefined下载地址:https://vscode.cdn.azure.cn/stable/1b8e8302e405050205e69b59abb3559592bb9e60/VSCodeUserSetup-x64-1.31.1.exe下载完后按引导安装即可2.1.1配置vs</div>
                    </li>
                    <li><a href="/article/1940710046203703296.htm"
                           title="【网络编程】EPOLL 事件触发机制的服务器" target="_blank">【网络编程】EPOLL 事件触发机制的服务器</a>
                        <span class="text-muted">啟明起鸣</span>
<a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a>
                        <div>文章目录业务拆解EPOLL机制介绍EPOLL的核心变量和函数EPOLL程序流程图C代码实现准备工作服务器代码代码运行效果总结推荐一个零声教育学习教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,点击立即学习:https:/</div>
                    </li>
                    <li><a href="/article/1940708028110139392.htm"
                           title="50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | ThemeClock(主题时钟)" target="_blank">50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | ThemeClock(主题时钟)</a>
                        <span class="text-muted">sunbyte</span>
<a class="tag" taget="_blank" href="/search/TailiwindCSS/1.htm">TailiwindCSS</a><a class="tag" taget="_blank" href="/search/%E5%AE%9E%E6%88%98%E6%8C%87%E5%8D%97/1.htm">实战指南</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/tailwindcss/1.htm">tailwindcss</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/1.htm">前端框架</a>
                        <div>我们继续50个小项目挑战!——ThemeClock组件仓库地址:https://github.com/SunACong/50-vue-projects项目预览地址:https://50-vue-projects.vercel.app/使用Vue3的CompositionAPI和语法结合TailwindCSS构建一个能够动态切换主题(深色与浅色)的时钟组件。该组件不仅显示当前时间(包括小时、分钟和秒</div>
                    </li>
                    <li><a href="/article/1940706768334155776.htm"
                           title="从数据抓取到智能分类:用 LangChain + 爬虫构建自动化工作流的实战笔记" target="_blank">从数据抓取到智能分类:用 LangChain + 爬虫构建自动化工作流的实战笔记</a>
                        <span class="text-muted">大模型之路</span>
<a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%A8%A1%E5%9E%8B%EF%BC%88LLM%EF%BC%89/1.htm">大模型(LLM)</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/langchain/1.htm">langchain</a>
                        <div>一、从人工到自动化的迫切需求在数字化时代,信息的快速获取与处理成为个人和组织高效运转的关键。然而,许多重复性强、耗时长且缺乏创造性的任务,如定期收集和整理网络信息并制作成特定格式的内容,依然占据着人们大量的时间和精力。本文作者就面临这样的困境:每两周需花费数小时访问多个大学网站,提取活动信息,手动将其整理成繁琐的HTML表格,并确保在Outlook中格式正确无误。这一过程不仅涉及大量枯燥的重复劳动</div>
                    </li>
                    <li><a href="/article/1940706387776565248.htm"
                           title="【web安全】远程命令执行(RCE)漏洞深度解析与攻防实践" target="_blank">【web安全】远程命令执行(RCE)漏洞深度解析与攻防实践</a>
                        <span class="text-muted">KPX</span>
<a class="tag" taget="_blank" href="/search/web%E5%AE%89%E5%85%A8/1.htm">web安全</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a><a class="tag" taget="_blank" href="/search/web%E5%AE%89%E5%85%A8/1.htm">web安全</a><a class="tag" taget="_blank" href="/search/windows/1.htm">windows</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E6%BC%8F%E6%B4%9E/1.htm">漏洞</a>
                        <div>目录摘要1.RCE漏洞概述1.1基本概念1.2漏洞危害等级2.RCE漏洞原理深度分析2.1漏洞产生条件2.2常见危险函数2.2.1PHP环境2.2.2Java环境2.2.3Python环境3.RCE利用技术进阶3.1基础注入技术扩展3.1.1命令分隔技术3.1.2参数注入技术3.2高级绕过技术3.2.1编码混淆3.2.2字符串拼接3.3盲注技术3.3.1时间延迟检测3.3.2DNS外带数据3.3.</div>
                    </li>
                    <li><a href="/article/1940706135157829632.htm"
                           title="SQLmap 使用指南:开启安全测试高效之旅" target="_blank">SQLmap 使用指南:开启安全测试高效之旅</a>
                        <span class="text-muted"></span>

                        <div>SQLmap作为一款强大的开源自动化SQL注入工具,在安全测试领域扮演着至关重要的角色,它能够精准检测并有效利用Web应用程序中潜藏的SQL注入漏洞。但请务必牢记,其使用必须严格限定在合法授权的范围内,以确保不触碰法律红线。安装SQLmap在Windows系统中安装SQLmap,首先要确保已成功安装Python环境。因为SQLmap是基于Python开发的,Python环境是其运行的基础。安装好P</div>
                    </li>
                    <li><a href="/article/1940706008397574144.htm"
                           title="Jupiter项目版本演进与技术架构深度解析" target="_blank">Jupiter项目版本演进与技术架构深度解析</a>
                        <span class="text-muted">齐飞锴Timothea</span>

                        <div>Jupiter项目版本演进与技术架构深度解析JupiterJupiter是一款性能非常不错的,轻量级的分布式服务框架项目地址:https://gitcode.com/gh_mirrors/jup/Jupiter项目概述Jupiter是一个高性能的分布式服务框架,专注于提供稳定可靠的RPC通信能力。从版本迭代历史可以看出,该项目在性能优化、功能完善和稳定性提升方面持续演进。本文将深入分析Jupite</div>
                    </li>
                    <li><a href="/article/1940701850206531584.htm"
                           title="Linux系统JDK 8下载与安装指南" target="_blank">Linux系统JDK 8下载与安装指南</a>
                        <span class="text-muted">丹力</span>

                        <div>本文还有配套的精品资源,点击获取简介:JavaDevelopmentKit(JDK)8是Java编程语言的重要开发工具包,包含了必要的工具和库以编译、调试和运行Java应用程序。JDK8在2014年发布,引入了Lambda表达式、StreamAPI、新的日期和时间API以及方法引用等特性,提升了开发效率和代码可读性。本文将指导读者如何在Linux系统上下载和安装JDK8,包括下载步骤、解压文件、移</div>
                    </li>
                    <li><a href="/article/1940700335949541376.htm"
                           title="Spring Boot + AI,真的有搞头吗?5大步骤带你轻松入门" target="_blank">Spring Boot + AI,真的有搞头吗?5大步骤带你轻松入门</a>
                        <span class="text-muted">墨瑾轩</span>
<a class="tag" taget="_blank" href="/search/%E4%B8%80%E8%B5%B7%E5%AD%A6%E5%AD%A6Java%E3%80%90%E4%B8%80%E3%80%91/1.htm">一起学学Java【一】</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a>
                        <div>关注墨瑾轩,带你探索编程的奥秘!超萌技术攻略,轻松晋级编程高手技术宝库已备好,就等你来挖掘订阅墨瑾轩,智趣学习不孤单即刻启航,编程之旅更有趣亲爱的小伙伴们,你们是否听说过SpringBoot和AI结合的消息?是不是觉得这两者听起来就像是天作之合?没错,SpringBoot和AI的结合确实能为我们带来许多意想不到的好处!今天,我们就来一起探讨如何在SpringBoot项目中集成AI功能,让你的应用更</div>
                    </li>
                    <li><a href="/article/1940698195113865216.htm"
                           title="C++泛型编程指南08 函数模板优先级匹配" target="_blank">C++泛型编程指南08 函数模板优先级匹配</a>
                        <span class="text-muted">丁金金_chihiro_修行</span>
<a class="tag" taget="_blank" href="/search/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B%E6%8C%87%E5%8D%97/1.htm">泛型编程指南</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/1.htm">泛型编程</a><a class="tag" taget="_blank" href="/search/%E6%A8%A1%E6%9D%BF/1.htm">模板</a><a class="tag" taget="_blank" href="/search/%E9%87%8D%E8%BD%BD%E5%86%B3%E8%AE%AE/1.htm">重载决议</a><a class="tag" taget="_blank" href="/search/%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8%E4%BC%98%E5%85%88%E7%BA%A7/1.htm">函数调用优先级</a>
                        <div>文章目录函数的不同修饰模板函数的不同修饰修饰带来的功能上的变化修饰带来的函数调用,模板实例化上的变化(函数/模板的重载决议)非模板类型(函数)匹配程度的排序总结查看普通函数的实现原始版本使用指针使用引用使用常量使用常量指针使用常量引用返回引用返回指针返回常量引用查看泛化函数的实现1.`intmax(int,int);`2.`intmax(constint*,constint*);`3.`intma</div>
                    </li>
                    <li><a href="/article/1940694786558193664.htm"
                           title="泛型编程之完美转发" target="_blank">泛型编程之完美转发</a>
                        <span class="text-muted">发如雪-ty</span>
<a class="tag" taget="_blank" href="/search/%E6%A8%A1%E6%9D%BF%E4%B8%8E%E6%B3%9B%E5%9E%8B%E7%BC%96%E7%A8%8B/1.htm">模板与泛型编程</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>首先简单介绍一下几个概念(1)直接调用:比如从main()主函数中调用funcLast()函数,这其实就叫做直接调用。(2)转发:从main()函数中调用funcMiddle()函数,通过funcMiddle()函数调用funcLast()函数,这就叫做转发,funcMiddle()函数被当作一个跳板函数。一般情况下跳板函数都写成一个函数模板。templatevoidfunc(T¶m){c</div>
                    </li>
                    <li><a href="/article/1940694154463997952.htm"
                           title="GPT4.0带记忆API源码" target="_blank">GPT4.0带记忆API源码</a>
                        <span class="text-muted">沐晨API</span>
<a class="tag" taget="_blank" href="/search/php/1.htm">php</a>
                        <div>以下为输出截图:需要在同路径中添加一个名为conversations的文件夹原本输出为英文,对接翻译接口沐晨API翻译:https://mcapi.muwl.xyz/api/fanyi2.php,可以自己改成别的,不过改了输出的格式不一样,代码也需要变动沐晨API:沐晨免费稳定API,沐晨收录站,欢迎前来申请代码json_encode($messages)]);//构建GET请求的查询字符串$ch</div>
                    </li>
                    <li><a href="/article/1940693271449759744.htm"
                           title="Python 3.11.6 Windows 64位版安装程序下载:轻松上手Python最新版本" target="_blank">Python 3.11.6 Windows 64位版安装程序下载:轻松上手Python最新版本</a>
                        <span class="text-muted">惠凯忱Montague</span>

                        <div>Python3.11.6Windows64位版安装程序下载:轻松上手Python最新版本去发现同类优质开源项目:https://gitcode.com/项目介绍在编程领域,Python无疑是一种极为流行且强大的编程语言。Python3.11.6Windows64位版安装程序的推出,为Windows用户提供了官方最新版本的安装便利。这个版本不仅包含了许多优化和新特性,而且确保了在64位Windows</div>
                    </li>
                    <li><a href="/article/1940691883902693376.htm"
                           title="【YOLOv11】ultralytics最新作品yolov11 AND 模型的训练、推理、验证、导出 以及 使用" target="_blank">【YOLOv11】ultralytics最新作品yolov11 AND 模型的训练、推理、验证、导出 以及 使用</a>
                        <span class="text-muted">Jackilina_Stone</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Deep/1.htm">Deep</a><a class="tag" taget="_blank" href="/search/Learning/1.htm">Learning</a><a class="tag" taget="_blank" href="/search/%E3%80%90%E6%94%B9%E8%BF%9B%E3%80%91YOLO%E7%B3%BB%E5%88%97/1.htm">【改进】YOLO系列</a><a class="tag" taget="_blank" href="/search/YOLO/1.htm">YOLO</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/1.htm">计算机视觉</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a>
                        <div>目录一ultralytics公司的最新作品YOLOV111yolov11的创新2安装YOLOv113PYTHONGuide二训练三验证四推理五导出模型六使用文档:https://docs.ultralytics.com/models/yolo11/代码链接:https://github.com/ultralytics/ultralyticsPerformanceMetrics</div>
                    </li>
                    <li><a href="/article/1940691885270036480.htm"
                           title="Netty案例:WebSocket开发网页版聊天室" target="_blank">Netty案例:WebSocket开发网页版聊天室</a>
                        <span class="text-muted">熙客</span>
<a class="tag" taget="_blank" href="/search/12_%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/1.htm">12_计算机网络</a><a class="tag" taget="_blank" href="/search/websocket/1.htm">websocket</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/1.htm">网络协议</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C/1.htm">网络</a>
                        <div>目录1、开发流程2、具体代码实现2.1添加依赖(pom.xml)2.2配置文件(application.yml)2.3配置类读取设置2.4Netty服务器实现2.5WebSocket初始化器和处理器2.6SpringBoot启动类2.7HTML5客户端(src/main/resources/static/chat.html)2.8启动与测试1、开发流程创建SpringBoot项目添加Netty依赖</div>
                    </li>
                    <li><a href="/article/1940690749305712640.htm"
                           title="python中常用函数表_Python列表中几个常用函数总结" target="_blank">python中常用函数表_Python列表中几个常用函数总结</a>
                        <span class="text-muted">weixin_39934613</span>
<a class="tag" taget="_blank" href="/search/python%E4%B8%AD%E5%B8%B8%E7%94%A8%E5%87%BD%E6%95%B0%E8%A1%A8/1.htm">python中常用函数表</a>
                        <div>1、append()方法用于在列表末尾添加新的对象。语法:list.append(obj)参数:list定义的列表obj所要添加到列表的对象例:list=['Microsoft','Amazon','Geogle']list.append('Apple')print(list)显示结果为:['Microsoft','Amazon','Geogle','Apple']2、extend()函数用于在列</div>
                    </li>
                    <li><a href="/article/1940689990358986752.htm"
                           title="分享一个 Cursor mdc 生成器,基于 Gemini 2.5,很实用!" target="_blank">分享一个 Cursor mdc 生成器,基于 Gemini 2.5,很实用!</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AFcursor%E5%90%8E%E7%AB%AF/1.htm">前端cursor后端</a>
                        <div>大家好,我是Immerse,一名独立开发者、内容创作者。关注公众号:#沉浸式趣谈,获取最新文章(更多内容只在公众号更新)个人网站:https://yaolifeng.com也同步更新。转载请在文章开头注明出处和版权信息。我会在这里分享关于编程、独立开发、AI干货、开源、个人思考等内容。如果本文对您有所帮助,欢迎动动小手指一键三连(点赞、评论、转发),给我一些支持和鼓励,谢谢!大部分小伙伴现在应该都</div>
                    </li>
                    <li><a href="/article/1940689489466814464.htm"
                           title="Python 与 面向对象编程(OOP)" target="_blank">Python 与 面向对象编程(OOP)</a>
                        <span class="text-muted">lanbing</span>
<a class="tag" taget="_blank" href="/search/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%EF%BC%88OOP%EF%BC%89/1.htm">面向对象(OOP)</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/1.htm">面向对象</a>
                        <div>Python是一种支持面向对象编程(OOP)的多范式语言,其OOP实现简洁灵活,但在某些设计选择上与传统OOP语言(如Java、C#)存在显著差异。以下是Python面向对象编程的核心特性、优势和局限性的全面解析:一、Python的OOP核心特性1.万物皆对象Python中所有数据类型(如整数、字符串)均为对象,继承自object基类。函数、模块、异常等也都是对象,可以赋值、传递或动态修改。例如n</div>
                    </li>
                    <li><a href="/article/1940688859771760640.htm"
                           title="【Python】Python —— 列表 (文末附思维导图)" target="_blank">【Python】Python —— 列表 (文末附思维导图)</a>
                        <span class="text-muted"></span>

                        <div>Python——列表1定义用于存储任意数目、任意类型的数据集合。List(列表)是Python内置的一种数据类型。标准语法格式:1.a=[10,20,30,40]2.a=[10,20,‘abc’,True]是一种有序的集合,可以随时增加或删除其中的元素。标识是中括号[]。2创建2.1基本语法创建a=[10,20,'yangyaqi','石家庄学院',True]a[10,20,‘yangyaqi’,</div>
                    </li>
                    <li><a href="/article/1940685959137914880.htm"
                           title="基于 WebGL 与 GIS 的智慧垃圾分类三维可视化技术方案" target="_blank">基于 WebGL 与 GIS 的智慧垃圾分类三维可视化技术方案</a>
                        <span class="text-muted">图扑可视化</span>
<a class="tag" taget="_blank" href="/search/%E6%95%B0%E5%AD%97%E5%AD%AA%E7%94%9F/1.htm">数字孪生</a><a class="tag" taget="_blank" href="/search/%E4%B8%89%E7%BB%B4%E5%8F%AF%E8%A7%86%E5%8C%96/1.htm">三维可视化</a><a class="tag" taget="_blank" href="/search/%E5%9E%83%E5%9C%BE%E5%88%86%E7%B1%BB/1.htm">垃圾分类</a><a class="tag" taget="_blank" href="/search/%E6%99%BA%E6%85%A7%E7%8E%AF%E5%8D%AB/1.htm">智慧环卫</a>
                        <div>图扑自主研发的HT可视化引擎,基于HTML5的WebGL与Canvas技术构建,形成了完整的2D/3D图形渲染体系。该引擎无需依赖第三方插件,通过纯JavaScript脚本调用API,即可实现跨平台的可视化交互体验,支持PC端、移动端及大屏终端的多屏协同。在三维渲染技术层面,引擎深度集成WebGL底层图形接口,构建了高效的轻量化处理体系。HT还支持3DTiles格式航拍倾斜摄影实景数据、城市建筑群</div>
                    </li>
                                <li><a href="/article/87.htm"
                                       title="面向对象面向过程" target="_blank">面向对象面向过程</a>
                                    <span class="text-muted">3213213333332132</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>面向对象:把要完成的一件事,通过对象间的协作实现。 
面向过程:把要完成的一件事,通过循序依次调用各个模块实现。 
我把大象装进冰箱这件事为例,用面向对象和面向过程实现,都是用java代码完成。 
 
1、面向对象 
 

package bigDemo.ObjectOriented;

/**
 * 大象类
 * 
 * @Description
 * @author FuJian</div>
                                </li>
                                <li><a href="/article/214.htm"
                                       title="Java Hotspot: Remove the Permanent Generation" target="_blank">Java Hotspot: Remove the Permanent Generation</a>
                                    <span class="text-muted">bookjovi</span>
<a class="tag" taget="_blank" href="/search/HotSpot/1.htm">HotSpot</a>
                                    <div>  
openjdk上关于hotspot将移除永久带的描述非常详细,http://openjdk.java.net/jeps/122 
  
JEP 122: Remove the Permanent Generation

Author	Jon Masamitsu
Organization	Oracle
Created	2010/8/15
Updated	2011/</div>
                                </li>
                                <li><a href="/article/341.htm"
                                       title="正则表达式向前查找向后查找,环绕或零宽断言" target="_blank">正则表达式向前查找向后查找,环绕或零宽断言</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1.htm">正则表达式</a>
                                    <div>向前查找和向后查找 
1. 向前查找:根据要匹配的字符序列后面存在一个特定的字符序列(肯定式向前查找)或不存在一个特定的序列(否定式向前查找)来决定是否匹配。.NET将向前查找称之为零宽度向前查找断言。 
    对于向前查找,出现在指定项之后的字符序列不会被正则表达式引擎返回。 
2. 向后查找:一个要匹配的字符序列前面有或者没有指定的</div>
                                </li>
                                <li><a href="/article/468.htm"
                                       title="BaseDao" target="_blank">BaseDao</a>
                                    <span class="text-muted">171815164</span>
<a class="tag" taget="_blank" href="/search/seda/1.htm">seda</a>
                                    <div>

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class BaseDao {

	public Conn</div>
                                </li>
                                <li><a href="/article/595.htm"
                                       title="Ant标签详解--Java命令" target="_blank">Ant标签详解--Java命令</a>
                                    <span class="text-muted">g21121</span>
<a class="tag" taget="_blank" href="/search/Java%E5%91%BD%E4%BB%A4/1.htm">Java命令</a>
                                    <div>        这一篇主要介绍与java相关标签的使用          终于开始重头戏了,Java部分是我们关注的重点也是项目中用处最多的部分。               
1</div>
                                </li>
                                <li><a href="/article/722.htm"
                                       title="[简单]代码片段_电梯数字排列" target="_blank">[简单]代码片段_电梯数字排列</a>
                                    <span class="text-muted">53873039oycg</span>
<a class="tag" taget="_blank" href="/search/%E4%BB%A3%E7%A0%81/1.htm">代码</a>
                                    <div>       今天看电梯数字排列是9 18 26这样呈倒N排列的,写了个类似的打印例子,如下:       
import java.util.Arrays;

public class 电梯数字排列_S3_Test {
	public static void main(S</div>
                                </li>
                                <li><a href="/article/849.htm"
                                       title="Hessian原理" target="_blank">Hessian原理</a>
                                    <span class="text-muted">云端月影</span>
<a class="tag" taget="_blank" href="/search/hessian%E5%8E%9F%E7%90%86/1.htm">hessian原理</a>
                                    <div>Hessian 原理分析 
 
 
 
 
 
一.      远程通讯协议的基本原理 
 
网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http 、 tcp 、 udp 等等, http 、 tcp 、 udp 都是在基于 Socket 概念上为某类应用场景而扩展出的传输协</div>
                                </li>
                                <li><a href="/article/976.htm"
                                       title="区分Activity的四种加载模式----以及Intent的setFlags" target="_blank">区分Activity的四种加载模式----以及Intent的setFlags</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/android/1.htm">android</a>
                                    <div>  
在多Activity开发中,有可能是自己应用之间的Activity跳转,或者夹带其他应用的可复用Activity。可能会希望跳转到原来某个Activity实例,而不是产生大量重复的Activity。 
这需要为Activity配置特定的加载模式,而不是使用默认的加载模式。 加载模式分类及在哪里配置 
Activity有四种加载模式: 
 
 standard 
 singleTop</div>
                                </li>
                                <li><a href="/article/1103.htm"
                                       title="hibernate几个核心API及其查询分析" target="_blank">hibernate几个核心API及其查询分析</a>
                                    <span class="text-muted">antonyup_2006</span>
<a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/.net/1.htm">.net</a><a class="tag" taget="_blank" href="/search/Hibernate/1.htm">Hibernate</a><a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a><a class="tag" taget="_blank" href="/search/%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86/1.htm">配置管理</a>
                                    <div>(一)  org.hibernate.cfg.Configuration类 
        读取配置文件并创建唯一的SessionFactory对象.(一般,程序初始化hibernate时创建.) 
        Configuration co</div>
                                </li>
                                <li><a href="/article/1230.htm"
                                       title="PL/SQL的流程控制" target="_blank">PL/SQL的流程控制</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/PL%2FSQL%E7%BC%96%E7%A8%8B/1.htm">PL/SQL编程</a><a class="tag" taget="_blank" href="/search/%E5%BE%AA%E7%8E%AF%E6%8E%A7%E5%88%B6/1.htm">循环控制</a>
                                    <div>PL/SQL也是一门高级语言,所以流程控制是必须要有的,oracle数据库的pl/sql比sqlserver数据库要难,很多pl/sql中有的sqlserver里面没有 
  
流程控制; 
   分支语句 if 条件 then 结果 else 结果  end if ;

  条件语句 case    when   条件  then  结果;

   循环语句  loop    </div>
                                </li>
                                <li><a href="/article/1357.htm"
                                       title="强大的Mockito测试框架" target="_blank">强大的Mockito测试框架</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/mockito/1.htm">mockito</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/1.htm">单元测试</a>
                                    <div>一.自动生成Mock类        在需要Mock的属性上标记@Mock注解,然后@RunWith中配置Mockito的TestRunner或者在setUp()方法中显示调用MockitoAnnotations.initMocks(this);生成Mock类即可。二.自动注入Mock类到被测试类  &nbs</div>
                                </li>
                                <li><a href="/article/1484.htm"
                                       title="精通Oracle10编程SQL(11)开发子程序" target="_blank">精通Oracle10编程SQL(11)开发子程序</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/plsql/1.htm">plsql</a>
                                    <div>/*
 *开发子程序
 */
--子程序目是指被命名的PL/SQL块,这种块可以带有参数,可以在不同应用程序中多次调用
--PL/SQL有两种类型的子程序:过程和函数
--开发过程
--建立过程:不带任何参数
CREATE OR REPLACE PROCEDURE out_time
IS
BEGIN
 DBMS_OUTPUT.put_line(systimestamp);
E</div>
                                </li>
                                <li><a href="/article/1611.htm"
                                       title="【EhCache一】EhCache版Hello World" target="_blank">【EhCache一】EhCache版Hello World</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/Hello+world/1.htm">Hello world</a>
                                    <div>本篇是EhCache系列的第一篇,总体介绍使用EhCache缓存进行CRUD的API的基本使用,更细节的内容包括EhCache源代码和设计、实现原理在接下来的文章中进行介绍 
  环境准备 
1.新建Maven项目 
  
2.添加EhCache的Maven依赖 
        <dependency>
            <groupId>ne</div>
                                </li>
                                <li><a href="/article/1738.htm"
                                       title="学习EJB3基础知识笔记" target="_blank">学习EJB3基础知识笔记</a>
                                    <span class="text-muted">白糖_</span>
<a class="tag" taget="_blank" href="/search/bean/1.htm">bean</a><a class="tag" taget="_blank" href="/search/Hibernate/1.htm">Hibernate</a><a class="tag" taget="_blank" href="/search/jboss/1.htm">jboss</a><a class="tag" taget="_blank" href="/search/webservice/1.htm">webservice</a><a class="tag" taget="_blank" href="/search/ejb/1.htm">ejb</a>
                                    <div>最近项目进入系统测试阶段,全赖袁大虾领导有力,保持一周零bug记录,这也让自己腾出不少时间补充知识。花了两天时间把“传智播客EJB3.0”看完了,EJB基本的知识也有些了解,在这记录下EJB的部分知识,以供自己以后复习使用。 
  
EJB是sun的服务器端组件模型,最大的用处是部署分布式应用程序。EJB (Enterprise JavaBean)是J2EE的一部分,定义了一个用于开发基</div>
                                </li>
                                <li><a href="/article/1865.htm"
                                       title="angular.bootstrap" target="_blank">angular.bootstrap</a>
                                    <span class="text-muted">boyitech</span>
<a class="tag" taget="_blank" href="/search/AngularJS/1.htm">AngularJS</a><a class="tag" taget="_blank" href="/search/AngularJS+API/1.htm">AngularJS API</a><a class="tag" taget="_blank" href="/search/angular%E4%B8%AD%E6%96%87api/1.htm">angular中文api</a>
                                    <div>angular.bootstrap 
描述:  
    手动初始化angular。 
    这个函数会自动检测创建的module有没有被加载多次,如果有则会在浏览器的控制台打出警告日志,并且不会再次加载。这样可以避免在程序运行过程中许多奇怪的问题发生。 
    使用方法:       angular .</div>
                                </li>
                                <li><a href="/article/1992.htm"
                                       title="java-谷歌面试题-给定一个固定长度的数组,将递增整数序列写入这个数组。当写到数组尾部时,返回数组开始重新写,并覆盖先前写过的数" target="_blank">java-谷歌面试题-给定一个固定长度的数组,将递增整数序列写入这个数组。当写到数组尾部时,返回数组开始重新写,并覆盖先前写过的数</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>

public class SearchInShiftedArray {

	/**
	 * 题目:给定一个固定长度的数组,将递增整数序列写入这个数组。当写到数组尾部时,返回数组开始重新写,并覆盖先前写过的数。
	 * 请在这个特殊数组中找出给定的整数。
	 * 解答:
	 * 其实就是“旋转数组”。旋转数组的最小元素见http://bylijinnan.iteye.com/bl</div>
                                </li>
                                <li><a href="/article/2119.htm"
                                       title="天使还是魔鬼?都是我们制造" target="_blank">天使还是魔鬼?都是我们制造</a>
                                    <span class="text-muted">ducklsl</span>
<a class="tag" taget="_blank" href="/search/%E7%94%9F%E6%B4%BB/1.htm">生活</a><a class="tag" taget="_blank" href="/search/%E6%95%99%E8%82%B2/1.htm">教育</a><a class="tag" taget="_blank" href="/search/%E6%83%85%E6%84%9F/1.htm">情感</a>
                                    <div>----------------------------剧透请原谅,有兴趣的朋友可以自己看看电影,互相讨论哦!!! 
    从厦门回来的动车上,无意中瞟到了书中推荐的几部关于儿童的电影。当然,这几部电影可能会另大家失望,并不是类似小鬼当家的电影,而是关于“坏小孩”的电影! 
    自己挑了两部先看了看,但是发现看完之后,心里久久不能平</div>
                                </li>
                                <li><a href="/article/2246.htm"
                                       title="[机器智能与生物]研究生物智能的问题" target="_blank">[机器智能与生物]研究生物智能的问题</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E7%94%9F%E7%89%A9/1.htm">生物</a>
                                    <div> 
 
      我想,人的神经网络和苍蝇的神经网络,并没有本质的区别...就是大规模拓扑系统和中小规模拓扑分析的区别.... 
 
 
      但是,如果去研究活体人类的神经网络和脑系统,可能会受到一些法律和道德方面的限制,而且研究结果也不一定可靠,那么希望从事生物神经网络研究的朋友,不如把</div>
                                </li>
                                <li><a href="/article/2373.htm"
                                       title="获取Android Device的信息" target="_blank">获取Android Device的信息</a>
                                    <span class="text-muted">dai_lm</span>
<a class="tag" taget="_blank" href="/search/android/1.htm">android</a>
                                    <div>
String phoneInfo = "PRODUCT: " + android.os.Build.PRODUCT;
phoneInfo += ", CPU_ABI: " + android.os.Build.CPU_ABI;
phoneInfo += ", TAGS: " + android.os.Build.TAGS;
ph</div>
                                </li>
                                <li><a href="/article/2500.htm"
                                       title="最佳字符串匹配算法(Damerau-Levenshtein距离算法)的Java实现" target="_blank">最佳字符串匹配算法(Damerau-Levenshtein距离算法)的Java实现</a>
                                    <span class="text-muted">datamachine</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8C%B9%E9%85%8D/1.htm">字符串匹配</a>
                                    <div>原文:http://www.javacodegeeks.com/2013/11/java-implementation-of-optimal-string-alignment.html------------------------------------------------------------------------------------------------------------</div>
                                </li>
                                <li><a href="/article/2627.htm"
                                       title="小学5年级英语单词背诵第一课" target="_blank">小学5年级英语单词背诵第一课</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/english/1.htm">english</a><a class="tag" taget="_blank" href="/search/word/1.htm">word</a>
                                    <div>long 长的 
show 给...看,出示 
mouth 口,嘴 
write 写 
  
use 用,使用 
take 拿,带来 
hand 手 
clever 聪明的 
  
often 经常 
wash 洗 
slow 慢的 
house 房子 
  
water 水 
clean 清洁的 
supper 晚餐 
out 在外 
  
face 脸,</div>
                                </li>
                                <li><a href="/article/2754.htm"
                                       title="macvim的使用实战" target="_blank">macvim的使用实战</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/mac/1.htm">mac</a><a class="tag" taget="_blank" href="/search/vim/1.htm">vim</a>
                                    <div>macvim用的是mac里面的vim, 只不过是一个GUI的APP, 相当于一个壳 
  
1. 下载macvim 
https://code.google.com/p/macvim/ 
  
2. 了解macvim 
:h               vim的使用帮助信息 
:h macvim  </div>
                                </li>
                                <li><a href="/article/2881.htm"
                                       title="java二分法查找" target="_blank">java二分法查找</a>
                                    <span class="text-muted">蕃薯耀</span>
<a class="tag" taget="_blank" href="/search/java%E4%BA%8C%E5%88%86%E6%B3%95%E6%9F%A5%E6%89%BE/1.htm">java二分法查找</a><a class="tag" taget="_blank" href="/search/%E4%BA%8C%E5%88%86%E6%B3%95/1.htm">二分法</a><a class="tag" taget="_blank" href="/search/java%E4%BA%8C%E5%88%86%E6%B3%95/1.htm">java二分法</a>
                                    <div>java二分法查找 
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
蕃薯耀 2015年6月23日 11:40:03 星期二 
http:/</div>
                                </li>
                                <li><a href="/article/3008.htm"
                                       title="Spring Cache注解+Memcached" target="_blank">Spring Cache注解+Memcached</a>
                                    <span class="text-muted">hanqunfeng</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/memcached/1.htm">memcached</a>
                                    <div>Spring3.1 Cache注解  
依赖jar包: 
<!-- simple-spring-memcached -->
		<dependency>
			<groupId>com.google.code.simple-spring-memcached</groupId>
			<artifactId>simple-s</div>
                                </li>
                                <li><a href="/article/3135.htm"
                                       title="apache commons io包快速入门" target="_blank">apache commons io包快速入门</a>
                                    <span class="text-muted">jackyrong</span>
<a class="tag" taget="_blank" href="/search/apache+commons/1.htm">apache commons</a>
                                    <div>原文参考 
http://www.javacodegeeks.com/2014/10/apache-commons-io-tutorial.html 
 
  Apache Commons IO 包绝对是好东西,地址在http://commons.apache.org/proper/commons-io/,下面用例子分别介绍: 
  1)  工具类 
  2</div>
                                </li>
                                <li><a href="/article/3262.htm"
                                       title="如何学习编程" target="_blank">如何学习编程</a>
                                    <span class="text-muted">lampcy</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a><a class="tag" taget="_blank" href="/search/C%2B%2B/1.htm">C++</a><a class="tag" taget="_blank" href="/search/c/1.htm">c</a>
                                    <div>首先,我想说一下学习思想.学编程其实跟网络游戏有着类似的效果.开始的时候,你会对那些代码,函数等产生很大的兴趣,尤其是刚接触编程的人,刚学习第一种语言的人.可是,当你一步步深入的时候,你会发现你没有了以前那种斗志.就好象你在玩韩国泡菜网游似的,玩到一定程度,每天就是练级练级,完全是一个想冲到高级别的意志力在支持着你.而学编程就更难了,学了两个月后,总是觉得你好象全都学会了,却又什么都做不了,又没有</div>
                                </li>
                                <li><a href="/article/3389.htm"
                                       title="架构师之spring-----spring3.0新特性的bean加载控制@DependsOn和@Lazy" target="_blank">架构师之spring-----spring3.0新特性的bean加载控制@DependsOn和@Lazy</a>
                                    <span class="text-muted">nannan408</span>
<a class="tag" taget="_blank" href="/search/Spring3/1.htm">Spring3</a>
                                    <div>1.前言。 
   如题。 
2.描述。 
   


@DependsOn用于强制初始化其他Bean。可以修饰Bean类或方法,使用该Annotation时可以指定一个字符串数组作为参数,每个数组元素对应于一个强制初始化的Bean。

@DependsOn({"steelAxe","abc"})
@Comp</div>
                                </li>
                                <li><a href="/article/3516.htm"
                                       title="Spring4+quartz2的配置和代码方式调度" target="_blank">Spring4+quartz2的配置和代码方式调度</a>
                                    <span class="text-muted">Everyday都不同</span>
<a class="tag" taget="_blank" href="/search/%E4%BB%A3%E7%A0%81/1.htm">代码</a><a class="tag" taget="_blank" href="/search/%E9%85%8D%E7%BD%AE/1.htm">配置</a><a class="tag" taget="_blank" href="/search/spring4/1.htm">spring4</a><a class="tag" taget="_blank" href="/search/quartz2.x/1.htm">quartz2.x</a><a class="tag" taget="_blank" href="/search/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/1.htm">定时任务</a>
                                    <div>前言:这些天简直被quartz虐哭。。因为quartz 2.x版本相比quartz1.x版本的API改动太多,所以,只好自己去查阅底层API…… 
  
quartz定时任务必须搞清楚几个概念: 
JobDetail——处理类 
Trigger——触发器,指定触发时间,必须要有JobDetail属性,即触发对象 
Scheduler——调度器,组织处理类和触发器,配置方式一般只需指定触发</div>
                                </li>
                                <li><a href="/article/3643.htm"
                                       title="Hibernate入门" target="_blank">Hibernate入门</a>
                                    <span class="text-muted">tntxia</span>
<a class="tag" taget="_blank" href="/search/Hibernate/1.htm">Hibernate</a>
                                    <div>  
前言 
  
使用面向对象的语言和关系型的数据库,开发起来很繁琐,费时。由于现在流行的数据库都不面向对象。Hibernate 是一个Java的ORM(Object/Relational Mapping)解决方案。 
  
Hibernte不仅关心把Java对象对应到数据库的表中,而且提供了请求和检索的方法。简化了手工进行JDBC操作的流程。 
  
如</div>
                                </li>
                                <li><a href="/article/3770.htm"
                                       title="Math类" target="_blank">Math类</a>
                                    <span class="text-muted">xiaoxing598</span>
<a class="tag" taget="_blank" href="/search/Math/1.htm">Math</a>
                                    <div>一、Java中的数字(Math)类是final类,不可继承。 
1、常数    PI:double圆周率 E:double自然对数    
2、截取(注意方法的返回类型)    double ceil(double d) 返回不小于d的最小整数 double floor(double d) 返回不大于d的整最大数   int round(float f) 返回四舍五入后的整数 long round</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>