基于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/1950233451282100224.htm"
                           title="python 读excel每行替换_Python脚本操作Excel实现批量替换功能" target="_blank">python 读excel每行替换_Python脚本操作Excel实现批量替换功能</a>
                        <span class="text-muted">weixin_39646695</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E8%AF%BBexcel%E6%AF%8F%E8%A1%8C%E6%9B%BF%E6%8D%A2/1.htm">读excel每行替换</a>
                        <div>Python脚本操作Excel实现批量替换功能大家好,给大家分享下如何使用Python脚本操作Excel实现批量替换。使用的工具Openpyxl,一个处理excel的python库,处理excel,其实针对的就是WorkBook,Sheet,Cell这三个最根本的元素~明确需求原始excel如下我们的目标是把下面excel工作表的sheet1表页A列的内容“替换我吧”批量替换为B列的“我用来替换的</div>
                    </li>
                    <li><a href="/article/1950233199242178560.htm"
                           title="x86-64汇编语言训练程序与实战" target="_blank">x86-64汇编语言训练程序与实战</a>
                        <span class="text-muted">十除以十等于一</span>

                        <div>本文还有配套的精品资源,点击获取简介:汇编语言是一种低级语言,与机器代码紧密相关,特别适用于编写系统级代码及性能要求高的应用。nasm编译器是针对x86和x86-64架构的汇编语言编译器,支持多种语法风格和指令集。项目Euler提供数学和计算机科学问题,鼓励编程技巧应用,前100个问题的答案可共享。x86-64架构扩展了寄存器数量并引入新指令,提升了数据处理效率。学习汇编语言能够深入理解计算机底层</div>
                    </li>
                    <li><a href="/article/1950233072825856000.htm"
                           title="三菱PLC全套学习资料及应用手册" target="_blank">三菱PLC全套学习资料及应用手册</a>
                        <span class="text-muted">good2know</span>

                        <div>本文还有配套的精品资源,点击获取简介:三菱PLC作为工业自动化领域的核心设备,其系列产品的学习和应用需要全面深入的知识。本次资料包为学习者提供从基础到进阶的全方位学习资源,包括各种型号PLC的操作手册、编程指南、软件操作教程以及实际案例分析,旨在帮助用户系统掌握PLC的编程语言、指令系统及在各类工业应用中的实施。1.三菱PLC基础知识入门1.1PLC的基本概念可编程逻辑控制器(PLC)是工业自动化</div>
                    </li>
                    <li><a href="/article/1950232316408295424.htm"
                           title="9、汇编语言编程入门:从环境搭建到简单程序实现" target="_blank">9、汇编语言编程入门:从环境搭建到简单程序实现</a>
                        <span class="text-muted">神经网络酱</span>
<a class="tag" taget="_blank" href="/search/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/1.htm">汇编语言</a><a class="tag" taget="_blank" href="/search/MEPIS/1.htm">MEPIS</a><a class="tag" taget="_blank" href="/search/GNU%E5%B7%A5%E5%85%B7%E9%93%BE/1.htm">GNU工具链</a>
                        <div>汇编语言编程入门:从环境搭建到简单程序实现1.数据存储介质问题解决在处理数据存储时,若要使用MEPIS系统,需确保有其可访问的存储介质。目前,MEPIS无法向采用NTFS格式(常用于Windows2000和XP工作站)的硬盘写入数据。不过,若硬盘采用FAT32格式,MEPIS就能进行写入操作。此外,MEPIS还能将文件写入软盘和大多数USB闪存驱动器。若工作站连接到局域网,还可通过FTP协议或挂载</div>
                    </li>
                    <li><a href="/article/1950229040682037248.htm"
                           title="48. 旋转图像 - 力扣(LeetCode)" target="_blank">48. 旋转图像 - 力扣(LeetCode)</a>
                        <span class="text-muted">Fiee-77</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%BB%84/1.htm">数组</a><a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%BB%84/1.htm">数组</a>
                        <div>题目:给定一个n×n的二维矩阵matrix表示一个图像。请你将图像顺时针旋转90度。你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。示例1:输入:matrix=[[1,2,3],[4,5,6],[7,8,9]]输出:[[7,4,1],[8,5,2],[9,6,3]]示例2:输入:matrix=[[5,1,9,11],[2,4,8,10],[13,3,6,</div>
                    </li>
                    <li><a href="/article/1950228285266915328.htm"
                           title="Git 与 GitHub 的对比与使用指南" target="_blank">Git 与 GitHub 的对比与使用指南</a>
                        <span class="text-muted">一念&</span>
<a class="tag" taget="_blank" href="/search/%E5%85%B6%E5%AE%83/1.htm">其它</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a><a class="tag" taget="_blank" href="/search/github/1.htm">github</a>
                        <div>Git与GitHub的对比与使用指南在软件开发中,Git和GitHub是两个密切相关但本质不同的工具。下面我将逐步解释它们的定义、区别、核心概念以及如何协同使用,确保内容真实可靠,基于广泛的技术实践。1.什么是Git?Git是一个分布式版本控制系统,由LinusTorvalds于2005年创建。它的核心功能是跟踪代码文件的变化,帮助开发者管理项目历史记录、协作和回滚错误。Git是开源的,可以在本地</div>
                    </li>
                    <li><a href="/article/1950227023192453120.htm"
                           title="MotionLCM 部署优化 踩坑解决bug" target="_blank">MotionLCM 部署优化 踩坑解决bug</a>
                        <span class="text-muted">AI算法网奇</span>
<a class="tag" taget="_blank" href="/search/aigc%E4%B8%8E%E6%95%B0%E5%AD%97%E4%BA%BA/1.htm">aigc与数字人</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%AE%9D%E5%85%B8/1.htm">深度学习宝典</a><a class="tag" taget="_blank" href="/search/%E6%96%87%E7%94%9Fmotion/1.htm">文生motion</a>
                        <div>目录依赖项windowstorchok:渲染黑白图问题解决:humanml3d:sentence-t5-large下载数据:报错:Nomodulenamed'sentence_transformers'继续报错:fromtransformers.integrationsimportCodeCarbonCallback解决方法:推理相关转mesh:module‘matplotlib.cm‘hasno</div>
                    </li>
                    <li><a href="/article/1950223878005518336.htm"
                           title="为什么焦虑、抑郁、自残的青少年越来越多?" target="_blank">为什么焦虑、抑郁、自残的青少年越来越多?</a>
                        <span class="text-muted">精神健康</span>

                        <div>很多家长觉得没缺孩子吃的穿的,他们有安稳的生活,他们有什么可焦虑、抑郁的,但现在的孩子,学习压力越来越大,每天休息的时间越来越少,出现焦虑抑郁是很正常的。从发展的角度看,青少年时期,人的身体、情绪,智力、人格都急剧发展,正从未成熟走向成熟,情绪起伏不定,易冲动,再者,由于缺乏生活经验,以及来自于家长、学校、社会的各种要求和压力,从而不知所措,心中的焦虑、恐惧、彷徨得不到及时的排解,从而导致心理上的</div>
                    </li>
                    <li><a href="/article/1950222345163567104.htm"
                           title="深入理解汇编语言子程序设计与系统调用" target="_blank">深入理解汇编语言子程序设计与系统调用</a>
                        <span class="text-muted">网安spinage</span>
<a class="tag" taget="_blank" href="/search/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/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/%E6%B1%87%E7%BC%96/1.htm">汇编</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>本文将全面解析汇编语言中子程序设计的核心技术以及系统调用的实现方法,涵盖参数传递的多种方式、堆栈管理、API调用等关键知识点,并提供实际案例演示。一、子程序设计:参数传递的艺术1.寄存器传参:高效简洁.386.modelflat,stdcalloptioncasemap:none.dataxdd5;定义变量ydd6sumdd?.code;函数定义:addxy1addxy1procpushebpmo</div>
                    </li>
                    <li><a href="/article/1950220052531834880.htm"
                           title="CodeFoeces-450B" target="_blank">CodeFoeces-450B</a>
                        <span class="text-muted">ss5smi</span>

                        <div>题目原题链接:B.JzzhuandSequences题意根据公式公式计算对应fn的值。参考了其他作者的代码和思路。找循环点。负数取余需要加取余数到>0为止才可取余。代码#includeusingnamespacestd;constintmod=1e9+7;intmain(){longlongf[10],x,y,n;cin>>x>>y>>n;x=(x+mod)%mod;y=(y+mod)%mod;f</div>
                    </li>
                    <li><a href="/article/1950219953118441472.htm"
                           title="编程算法:技术创新的引擎与业务增长的核心驱动力" target="_blank">编程算法:技术创新的引擎与业务增长的核心驱动力</a>
                        <span class="text-muted"></span>

                        <div>在数字经济时代,算法已成为推动技术创新与业务增长的隐形引擎。从存内计算突破冯·诺依曼瓶颈,到动态规划优化万亿级金融交易,编程算法正在重塑产业竞争格局。一、存内计算:突破冯·诺依曼瓶颈的算法革命1.1存内计算的基本原理传统计算架构中90%的能耗消耗在数据搬运上。存内计算(Processing-in-Memory)通过直接在存储单元执行计算,实现能效10-100倍提升:#传统计算vs存内计算能耗模型i</div>
                    </li>
                    <li><a href="/article/1950218946015719424.htm"
                           title="图论算法经典题目解析:DFS、BFS与拓扑排序实战" target="_blank">图论算法经典题目解析:DFS、BFS与拓扑排序实战</a>
                        <span class="text-muted">周童學</span>
<a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/1.htm">数据结构与算法</a><a class="tag" taget="_blank" href="/search/%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88/1.htm">深度优先</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%9B%BE%E8%AE%BA/1.htm">图论</a>
                        <div>图论算法经典题目解析:DFS、BFS与拓扑排序实战图论问题是算法面试中的高频考点,本博客将通过四道LeetCode经典题目(均来自"Top100Liked"题库),深入讲解图论的核心算法思想和实现技巧。涵盖DFS、BFS、拓扑排序和前缀树等知识点,每道题配有Java实现和易错点分析。1.岛屿数量(DFS遍历)问题描述给定一个由'1'(陆地)和'0'(水)组成的二维网格,计算岛屿的数量。岛屿由水平或</div>
                    </li>
                    <li><a href="/article/1950213272120651776.htm"
                           title="Redis + Caffeine 实现高效的两级缓存架构" target="_blank">Redis + Caffeine 实现高效的两级缓存架构</a>
                        <span class="text-muted">周童學</span>
<a class="tag" taget="_blank" href="/search/Java/1.htm">Java</a><a class="tag" taget="_blank" href="/search/%E7%BC%93%E5%AD%98/1.htm">缓存</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a>
                        <div>Redis+Caffeine实现高效的两级缓存架构引言在现代高并发系统中,缓存是提升系统性能的关键组件之一。传统的单一缓存方案往往难以同时满足高性能和高可用性的需求。本文将介绍如何结合Redis和Caffeine构建一个高效的两级缓存系统,并通过三个版本的演进展示如何逐步优化代码结构。项目源代码:github地址、gitee地址两级缓存架构概述两级缓存通常由本地缓存(如Caffeine)和分布式缓</div>
                    </li>
                    <li><a href="/article/1950208107430866944.htm"
                           title="python笔记14介绍几个魔法方法" target="_blank">python笔记14介绍几个魔法方法</a>
                        <span class="text-muted">抢公主的大魔王</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>python笔记14介绍几个魔法方法先声明一下各位大佬,这是我的笔记。如有错误,恳请指正。另外,感谢您的观看,谢谢啦!(1).__doc__输出对应的函数,类的说明文档print(print.__doc__)print(value,...,sep='',end='\n',file=sys.stdout,flush=False)Printsthevaluestoastream,ortosys.std</div>
                    </li>
                    <li><a href="/article/1950207097413103616.htm"
                           title="微信公众号回调java_处理微信公众号消息回调" target="_blank">微信公众号回调java_处理微信公众号消息回调</a>
                        <span class="text-muted">weixin_39607620</span>
<a class="tag" taget="_blank" href="/search/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7%E5%9B%9E%E8%B0%83java/1.htm">微信公众号回调java</a>
                        <div>1、背景在上一节中,咱们知道如何接入微信公众号,可是以后公众号会与咱们进行交互,那么微信公众号如何通知到咱们本身的服务器呢?咱们知道咱们接入的时候提供的url是GET/mp/entry,那么公众号以后产生的事件将会以POST/mp/entry发送到咱们本身的服务器上。html2、代码实现,此处仍是使用weixin-java-mp这个框架实现一、引入weixin-java-mpcom.github.</div>
                    </li>
                    <li><a href="/article/1950206970766094336.htm"
                           title="C++编程基础与面向对象概念解析" target="_blank">C++编程基础与面向对象概念解析</a>
                        <span class="text-muted">侯昂</span>
<a class="tag" taget="_blank" href="/search/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B/1.htm">面向对象编程</a><a class="tag" taget="_blank" href="/search/C%2B%2B%E8%AF%AD%E6%B3%95/1.htm">C++语法</a><a class="tag" taget="_blank" href="/search/%E5%87%BD%E6%95%B0/1.htm">函数</a><a class="tag" taget="_blank" href="/search/%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/1.htm">类与对象</a><a class="tag" taget="_blank" href="/search/%E7%BB%A7%E6%89%BF%E4%B8%8E%E5%A4%9A%E6%80%81%E6%80%A7/1.htm">继承与多态性</a>
                        <div>C++编程基础与面向对象概念解析背景简介C++是一种广泛使用的面向对象编程语言,它允许开发者创建高效、灵活且功能强大的程序。本文基于《C++Primer》一书的章节内容,深入解析C++的核心概念和面向对象编程原则,旨在帮助读者构建扎实的C++编程基础。面向对象编程的原则软件危机与进化介绍了软件危机的产生和软件进化的必要性,强调了面向对象编程(OOP)在应对这些问题中的优势。面向对象编程范式讨论了面</div>
                    </li>
                    <li><a href="/article/1950204954295726080.htm"
                           title="Anaconda 和 Miniconda:功能详解与选择建议" target="_blank">Anaconda 和 Miniconda:功能详解与选择建议</a>
                        <span class="text-muted">古月฿</span>
<a class="tag" taget="_blank" href="/search/python%E5%85%A5%E9%97%A8/1.htm">python入门</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/conda/1.htm">conda</a>
                        <div>Anaconda和Miniconda详细介绍一、Anaconda的详细介绍1.什么是Anaconda?Anaconda是一个开源的包管理和环境管理工具,在数据科学、机器学习以及科学计算领域发挥着关键作用。它以Python和R语言为基础,为用户精心准备了大量预装库和工具,极大地缩短了搭建数据科学环境的时间。对于那些想要快速开展数据分析、模型训练等工作的人员来说,Anaconda就像是一个一站式的“数</div>
                    </li>
                    <li><a href="/article/1950204701714739200.htm"
                           title="环境搭建 | Python + Anaconda / Miniconda + PyCharm 的安装、配置与使用" target="_blank">环境搭建 | Python + Anaconda / Miniconda + PyCharm 的安装、配置与使用</a>
                        <span class="text-muted"></span>

                        <div>本文将分别介绍Python、Anaconda/Miniconda、PyCharm的安装、配置与使用,详细介绍Python环境搭建的全过程,涵盖Python、Pip、PythonLauncher、Anaconda、Miniconda、Pycharm等内容,以官方文档为参照,使用经验为补充,内容全面而详实。由于图片太多,就先贴一个无图简化版吧,详情请查看Python+Anaconda/Minicond</div>
                    </li>
                    <li><a href="/article/1950202938265759744.htm"
                           title="你竟然还在用克隆删除?Conda最新版rename命令全攻略!" target="_blank">你竟然还在用克隆删除?Conda最新版rename命令全攻略!</a>
                        <span class="text-muted">曦紫沐</span>
<a class="tag" taget="_blank" href="/search/Python%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/1.htm">Python基础知识</a><a class="tag" taget="_blank" href="/search/conda/1.htm">conda</a><a class="tag" taget="_blank" href="/search/%E8%99%9A%E6%8B%9F%E7%8E%AF%E5%A2%83%E7%AE%A1%E7%90%86/1.htm">虚拟环境管理</a>
                        <div>文章摘要Conda虚拟环境管理终于迎来革命性升级!本文揭秘Conda4.9+版本新增的rename黑科技,彻底告别传统“克隆+删除”的繁琐操作。从命令解析到实战案例,手把手教你如何安全高效地重命名Python虚拟环境,附带版本检测、环境迁移、故障排查等进阶技巧,助你提升开发效率10倍!一、颠覆认知:Conda居然自带重命名功能?很多开发者仍停留在“Conda无法直接重命名环境”的认知阶段,实际上自</div>
                    </li>
                    <li><a href="/article/1950202684451647488.htm"
                           title="[spring6: Mvc-网关]-源码解析" target="_blank">[spring6: Mvc-网关]-源码解析</a>
                        <span class="text-muted"></span>

                        <div>推荐阅读:[spring6:Mvc-函数式编程]-源码解析GatewayServerMvcAutoConfiguration@AutoConfiguration(after={HttpClientAutoConfiguration.class,RestTemplateAutoConfiguration.class,RestClientAutoConfiguration.class,FilterAu</div>
                    </li>
                    <li><a href="/article/1950202054706262016.htm"
                           title="centos7安装配置 Anaconda3" target="_blank">centos7安装配置 Anaconda3</a>
                        <span class="text-muted"></span>

                        <div>Anaconda是一个用于科学计算的Python发行版,Anaconda于Python,相当于centos于linux。下载[root@testsrc]#mwgethttps://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-5.2.0-Linux-x86_64.shBegintodownload:Anaconda3-5.2.0-L</div>
                    </li>
                    <li><a href="/article/1950202054219722752.htm"
                           title="Pandas:数据科学的超级瑞士军刀" target="_blank">Pandas:数据科学的超级瑞士军刀</a>
                        <span class="text-muted">科技林总</span>
<a class="tag" taget="_blank" href="/search/DeepSeek%E5%AD%A6AI/1.htm">DeepSeek学AI</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
                        <div>**——从零基础到高效分析的进化指南**###**一、Pandas诞生:数据革命的救世主****2010年前的数据分析噩梦**:```python#传统Python处理表格数据data=[]forrowincsv_file:ifrow[3]>100androw[2]=="China":data.append(float(row[5])#代码冗长易错!```**核心痛点**:-Excel处理百万行崩</div>
                    </li>
                    <li><a href="/article/1950201171369062400.htm"
                           title="Zread.AI:一键将GitHub项目转化为结构化中文手册的AI代码维基工具" target="_blank">Zread.AI:一键将GitHub项目转化为结构化中文手册的AI代码维基工具</a>
                        <span class="text-muted"></span>

                        <div>Zread.AI:一键将GitHub项目转化为结构化中文手册的AI代码维基工具文章来源:PoixeAI文章目录Zread.AI工具概述核心功能优势亮点典型应用场景上手指南注意事项官网地址Zread.AI由智谱Z.ai推出,是一款面向开发者的AI代码维基工具,可在几秒内把任何公开GitHub仓库转化为结构化中文手册,并通过独家Buzz面板聚合commits、issues与相关新闻,让项目脉搏一目了然</div>
                    </li>
                    <li><a href="/article/1950200667587014656.htm"
                           title="学C++的五大惊人好处" target="_blank">学C++的五大惊人好处</a>
                        <span class="text-muted"></span>

                        <div>为什么要学c++学c++有什么用学习c++的好处有1.中考可以加分2.高考可能直接录取3.就业广且工资高4.在未来30--50年c++一定是一个很受欢迎的职业5.c++成功的例子deepsick等AI智能C++语言兼备编程效率和编译运行效率的语言C++语言是C语言功能增强版,在c语言的基础上添加了面向对象编程和泛型编程的支持既继承了C语言高效,简洁,快速和可移植的传统,又具备类似Java、Go等其</div>
                    </li>
                    <li><a href="/article/1950200162810916864.htm"
                           title="Selenium基础教程" target="_blank">Selenium基础教程</a>
                        <span class="text-muted">lemontree1945</span>
<a class="tag" taget="_blank" href="/search/selenium/1.htm">selenium</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/1.htm">测试工具</a>
                        <div>1.Selenium环境安装1.1浏览器安装Chrome和ChromeDriver下载地址:https://googlechromelabs.github.io/chrome-for-testing/注意:驱动版本号要和浏览器版本号一致;安装后关闭浏览器自动更新:services.msc:打开系统服务找到和google相关的服务,全部修改为禁用1.2安装第三方库seleniumpipinstall</div>
                    </li>
                    <li><a href="/article/1950199910724857856.htm"
                           title="机器学习必备数学与编程指南:从入门到精通" target="_blank">机器学习必备数学与编程指南:从入门到精通</a>
                        <span class="text-muted">a小胡哦</span>
<a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%9F%BA%E7%A1%80/1.htm">机器学习基础</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
                        <div>一、机器学习核心数学基础1.线性代数(神经网络的基础)必须掌握:矩阵运算(乘法、转置、逆)向量空间与线性变换特征值分解与奇异值分解(SVD)为什么重要:神经网络本质就是矩阵运算学习技巧:用NumPy实际操作矩阵运算2.概率与统计(模型评估的关键)核心概念:条件概率与贝叶斯定理概率分布(正态、泊松、伯努利)假设检验与p值应用场景:朴素贝叶斯、A/B测试3.微积分(优化算法的基础)重点掌握:导数与偏导</div>
                    </li>
                    <li><a href="/article/1950198270882017280.htm"
                           title="《UNIX网络编程卷1:套接字联网API》第8章:基本UDP套接字编程深度解析" target="_blank">《UNIX网络编程卷1:套接字联网API》第8章:基本UDP套接字编程深度解析</a>
                        <span class="text-muted"></span>

                        <div>《UNIX网络编程卷1:套接字联网API》第8章:基本UDP套接字编程深度解析(8000字图文实战)一、UDP协议核心特性与编程模型1.1UDP协议设计哲学UDP(UserDatagramProtocol)是面向无连接的传输层协议(图1),其核心特征包括:无连接通信:无需三次握手,直接发送数据报尽最大努力交付:不保证可靠性、不维护连接状态报文边界保留:接收方读取的数据与发送方写入完全一致低开销高效</div>
                    </li>
                    <li><a href="/article/1950195876991397888.htm"
                           title="【Jupyter】个人开发常见命令" target="_blank">【Jupyter】个人开发常见命令</a>
                        <span class="text-muted">TIM老师</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Pycharm/1.htm">Pycharm</a><a class="tag" taget="_blank" href="/search/%26amp%3B/1.htm">&</a><a class="tag" taget="_blank" href="/search/VSCode/1.htm">VSCode</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/Jupyter/1.htm">Jupyter</a>
                        <div>1.查看python版本importsysprint(sys.version)2.ipynb/py文件转换jupyternbconvert--topythonmy_file.ipynbipynb转换为mdjupyternbconvert--tomdmy_file.ipynbipynb转为htmljupyternbconvert--tohtmlmy_file.ipynbipython转换为pdfju</div>
                    </li>
                    <li><a href="/article/1950195246893690880.htm"
                           title="30 岁转行编程来得及吗?这位宝妈的经历告诉你答案" target="_blank">30 岁转行编程来得及吗?这位宝妈的经历告诉你答案</a>
                        <span class="text-muted">大力出奇迹985</span>
<a class="tag" taget="_blank" href="/search/react.js/1.htm">react.js</a>
                        <div>30岁转行编程是否可行?本文通过一位宝妈的真实经历给出答案。这位宝妈在30岁时,因职业发展瓶颈和对编程的兴趣,毅然决定转行。她克服了学习中的诸多困难,平衡家庭与学习,最终成功入职科技公司。文章详细讲述了她的转行动机、学习过程、求职经历及收获,证明了只要有决心和正确方法,30岁转行编程完全来得及,为有类似想法的人提供了实用参考。正文一、转行的契机:职业瓶颈与心中热爱的碰撞30岁的林悦(化名)曾是一家</div>
                    </li>
                    <li><a href="/article/1950194868303228928.htm"
                           title="免费编程课程大汇总:从入门到精通的一站式资源" target="_blank">免费编程课程大汇总:从入门到精通的一站式资源</a>
                        <span class="text-muted">大力出奇迹985</span>
<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%A4%A7%E6%95%B0%E6%8D%AE/1.htm">大数据</a>
                        <div>在数字化时代,编程已成为一项至关重要的技能,无论是为了职业发展还是个人兴趣,学习编程都极具价值。本文精心汇总了丰富的免费编程课程资源,涵盖从基础入门到精通的各个阶段。通过全面介绍如Coursera、edX等在线学习平台,Codecademy、freeCodeCamp等交互式学习网站,以及B站、网易云课堂等视频课程平台的免费课程,为编程学习者提供了一站式的资源指南,帮助读者轻松开启编程学习之旅,逐步</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>