0%

福利专区

摄影基本原理

在摄影中,通常使用照相机或者照相暗盒(Camera obscura)作为照相设备,利用光学胶卷或者数码存储卡作为记录介质。但也有例外,比如曼·雷的实物投影法(rayographs)就是利用影子在相纸上成像,而不需要用到照相机。 多数照相是用一系列的透镜组成的镜头,将光线折射聚焦后在胶片或者 CCD 光学感应介质,胶片本身也是储存介质,以化学变化储存讯号。而 CCD 则会将光转换为电子讯号后,以数码存储卡储存。储存的讯号可透过某些方式,还原成光学信息后,用相片或显示器观赏。除了透镜构成的镜头,少数镜头是用反射镜或反射镜搭配透镜所构成。

摄影艺术

摄影艺术是一门较为年轻的艺术门类,它是紧紧伴随着每个时代高新科技发展而发展着。摄影艺术是一种对现实高度概括;是来源于生活而高于生活的影像工作方式;是一种高贵的雅文化。摄影艺术就是作者的一种表达,正如说话是一种表达、写作是一种表达一样。摄影的表达方式多种多样,可以“风光”、“静物”、“人像”,也可以“纪实”、“民俗”、“观念”,表达方式没有高下之分。

摄影技术的学习不仅仅是学习光圈、焦距、曝光、色温、反差、感光度、白平衡之类的技术性知识,也不仅仅是学习构图、造型、色彩、光影等等美学知识,还有学习中外优秀摄影师的作品,看摄影师的画册和论著啦,等等。此外,还要学习一些诸如哲学、人类学、社会学、历史学、艺术史之类似乎与摄影无甚直接关系的学问。

wallpaper_5255573

视频下载

视频下载

PHP

关于PHP

PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言。语法吸收了C语言、Java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。PHP 独特的语法混合了C、Java、Perl以及PHP自创的语法。它可以比CGI或者Perl更快速地执行动态网页。用PHP做出的动态页面与其他的编程语言相比,PHP是将程序嵌入到HTML(标准通用标记语言下的一个应用)文档中去执行,执行效率比完全生成HTML标记的CGI要高许多;PHP还可以执行编译后代码,编译可以达到加密和优化代码运行,使代码运行更快。 在此提供PHP视频教程,视频来源:兄弟连教育

关于THINKPHP

    ThinkPHP是为了简化企业级应用开发和敏捷WEB应用开发而诞生的。最早诞生于2006年初,2007年元旦正式更名为ThinkPHP,并且遵循Apache2开源协议发布。ThinkPHP从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时,也注重易用性。并且拥有众多原创功能和特性,在社区团队的积极参与下,在易用性、扩展性和性能方面不断优化和改进。

    ThinkPHP是一个快速、兼容而且简单的轻量级国产PHP开发框架,诞生于2006年初,原名FCS,2007年元旦正式更名为ThinkPHP,遵循Apache2开源协议发布,从Struts结构移植过来并做了改进和完善,同时也借鉴了国外很多优秀的框架和模式,使用面向对象的开发结构和MVC模式,融合了Struts的思想和TagLib(标签库)、RoR的ORM映射和ActiveRecord模式。

ThinkPHP可以支持windows/Unix/Liunx等服务器环境,正式版需要PHP5.0以上版本支持,支持MySql、PgSQL、Sqlite以及PDO等多种数据库,ThinkPHP框架本身没有什么特别模块要求,具体的应用系统运行环境要求视开发所涉及的模块。

作为一个整体开发解决方案,ThinkPHP能够解决应用开发中的大多数需要,因为其自身包含了底层架构、兼容处理、基类库、数据库访问层、模板引擎、缓存机制、插件机制、角色认证、表单处理等常用的组件,并且对于跨版本、跨平台和跨数据库移植都比较方便。并且每个组件都是精心设计和完善的,应用开发过程仅仅需要关注您的业务逻辑。

关于兄弟连

兄弟连成立于2006年,专注于IT技术培训,是国内最早及最大的PHP/LAMP技术专业培训学校。 兄弟连现已开设PHP/Java/Android/IOS/手游/云计算/UI等多学科,累计培养逾万名学员,2014年学员就业平均起薪高达5500元+。 兄弟连已是第9个年头,这条路虽历尽艰辛,但我们痴心不改。我们就是想让学员们知道:不是所有的培训机构都是骗人的! 在兄弟连,你可以找到自我、重拾自信;在兄弟连,你会每天渴求成长,学到深夜; 在兄弟连,你把学习当成一种习惯;在兄弟连,你有更多的兄弟姐妹; 在兄弟连,有陪你一起熬夜的老师;在兄弟连,你会被“狠狠”的爱着…… 兄弟连已在北京、上海和广州设立校区,今后几年内将会陆续在成都、西安等地建设校区,每年有数十万名学员受益于兄弟连教育的职业培训、教学视频、网络公开课。

兄弟连

“我们不仅仅是老师,我们是学员的梦想守护者与职场引路人。” 我们不敢妄言改变中国教育,只是低下头认认真真做教育。兄弟连没有做什么惊天动地的大事,我们就是把别人不愿做的脏活累活做到极致,做教育就是需要这种工匠精神。 在中国,选择职业培训的学生,一定是对自己未来有憧憬、想改变命运的有志青年。主观上有学习的欲望,客观上自控能力差,需要外力协助其改变。 教学靠谱/变态严管/职业素养课我们的核心竞争力。 培训结束会有脱胎换骨的感觉,怕死别来兄弟连!

感谢 @兄弟连教育

VERSION:THINKPHP 版本 3.1.2

视频下载

Java

一、安装jdk

1)下载jdk1.7

下载地址 自己定义一个目录安装,一步步安装下来,我是安装到了D盘,如图所示:

2)设置环境变量

我的电脑右击点属性,再点高级系统设置 点击环境变量进行环境变量配置,如图所示: 配置方法一: 1)配置时找到系统变量,找到path变量,如果没有则新建。 在变量名填Path,变量值填 D:\jdk1.7\bin;注意要加分号! 如图所示: 2)同理,找到变量名classpath,没有就新建一个classpath 变量值填D:\jdk1.7\lib;. 配制方法二:

1)新建java_home变量 变量名:java_home 变量值:D:\jdk1.7; 2)变量名:path 变量值:%java_home%\bin; 3)变量名:classpath 变量值:%java_home%\lib;

这个是以后配置时直接引用java_home比较方便 这样jdk的安装和环境变量的配置就完成啦! 测试是否成功:在命令行输入java -version 如果出现下列字符则说明成功

二、Eclipse的下载

注意:此处提供的3.3版本,若版本太高,没有与Eclipse相对应的lomboz版本则无法配置,目前lomboz最高是3.3版本 下载地址 注意:如果你已经有了3.3版本,那么可以跳过此步。 我新建了一个JspEclipse文件夹,解压到此文件夹如图所示: 首先进行汉化 语言包下载 下载后新建一个eclipse_plugins文件夹,解压到此文件夹下,创建这个文件夹的目的是把所有下载的插件都保存到这里,便于管理 目录层次如图所示,language下有eclipse,eclipse下有features和plugins文件夹 在原先的JspEclipse下的eclipse文件夹下新建一个links文件夹,再新建文本文件,扩展名为link,内容如图所示: 这样汉化就完成了

三、配置lomboz

下载地址 同样下载解压到原先创建的eclipse_plugins文件夹下,如图所示: 同上在原先的JspEclipse下的eclipse文件夹下新建一个links文件夹,再新建文本文件,扩展名为link,内容如图所示: 这样lomboz就配置完成啦,启动一下试试! 此处显示中文,说明汉化成功! 小问题的解决:或许有的人会显示这样的页面: 那么下载 eclipse.ini 这个文件,把目录下的eclipse.ini文件替换掉。用记事本打开这个文件显示如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-startup
plugins\org.eclipse.equinox.launcher_1.0.0.v20070606.jar
--launcher.library
plugins\org.eclipse.equinox.launcher.win32.win32.x86_1.0.0.v20070523
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
128m
-vm
D:\java Jdk 1.6\bin\javaw.exe
-vmargs
-Xms40m
-Xmx384m
-Djava.net.preferIPv4Stack=true

-startup是启动所需要的jar文件,这里我把相对路径配好了 -luncher.library我也把相对路径配好了 -xxmaxpermsize是最大内存限制,如果这个数值过大也会出现上述情况,我改成了128m -vm 这里是相对的jdk环境设置,这个要设置成你自己的jdk环境,自己写一个路径,在bin目录下,一个javaw.exe文件,如果还不行,那么可能是jdk的版本导致的,我换用了jdk1.6测试成功!如果1.7不能用的话,就换用1.6版本的jdk吧! 好,小问题暂告一段落,看下图。 如果你能新建Dynamic Web Project说明你已经全配置成功啦!欢呼吧骚年们!

四、配置tomcat

下载地址 这个是免安装版,可以直接下载解压,我放在了D盘 tomcat环境变量的配置: 1,新建变量名:CATALINA_BASE,变量值:D:\tomcat 6.0\apache-tomcat-6.0.29 2,新建变量名:CATALINA_HOME,变量值:D:\tomcat 6.0\apache-tomcat-6.0.29 【1和2中的路径根据你存放的目录不同而不同】 3,打开PATH,添加变量值:%CATALINA_HOME%\lib;%CATALINA_HOME%\bin 启动tomcat: 在开始菜单输入cmd,然后cd命令定位到tomcat的bin目录,如图 然后输入 startup.bat命令,这时tomcat就启动起来了,会弹出一个新的窗口,那个就是tomcat 下面这个图就是tomcat: 下面进行测试:在浏览器输入http://localhost:8080/ 看看能否出现tomcat界面,如果出现了,那么说明成功了!如果不行,检查环境变量的配置。

五、tomcat嵌入eclipse

打开eclipse,窗口—>首选项,界面如下: 点选服务器—>运行时环境—>添加,选择Apach—>tomcat6.0,完成 创建之后编辑,添加tomcat的目录,名称自己随便取,jre用缺省jre 这样在eclipse下方就出现了一个tomcat了,绿色的箭头就是启动,红色的方块是停止 可以点击运行tomcat,其中点下方标记的红色处,可以对tomcat进行详细配置,默认配置即可,不用更改了

六、测试项目

新建一个Dynamic Web Project项目 点下一步,我取名为test,服务器选刚才创建的tomcat6.0,然后下一步,下一步,直到完成就好了 在webcontent目录下面新建一个jsp文件,我的叫a.jsp 我在body区输入了My First Jsp 右击该文件,在服务器上运行,选择tomcat,然后结果如图所示。恭喜你,所有配置都成功啦! 点选eclipse的窗口,然后web浏览器,选default system web browser,即系统默认浏览器,就可以用自己的浏览器打开界面啦。如图所示

Python

2022 年最新 Python3 网络爬虫教程

大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

教程请移步:

【2022 版】Python3 网络爬虫学习教程

如下为原文。

上一节我们介绍了正则表达式,它的内容其实还是蛮多的,如果一个正则匹配稍有差池,那可能程序就处在永久的循环之中,而且有的小伙伴们也对写正则表达式的写法用得不熟练,没关系,我们还有一个更强大的工具,叫Beautiful Soup,有了它我们可以很方便地提取出HTML或XML标签中的内容,实在是方便,这一节就让我们一起来感受一下Beautiful Soup的强大吧。

1. Beautiful Soup的简介

简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据。官方解释如下:

Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。 Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。 Beautiful Soup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。

废话不多说,我们来试一下吧~

2. Beautiful Soup 安装

Beautiful Soup 3 目前已经停止开发,推荐在现在的项目中使用Beautiful Soup 4,不过它已经被移植到BS4了,也就是说导入时我们需要 import bs4 。所以这里我们用的版本是 Beautiful Soup 4.3.2 (简称BS4),另外据说 BS4 对 Python3 的支持不够好,不过我用的是 Python2.7.7,如果有小伙伴用的是 Python3 版本,可以考虑下载 BS3 版本。 可以利用 pip 或者 easy_install 来安装,以下两种方法均可

1
easy_install beautifulsoup4
1
pip install beautifulsoup4

如果想安装最新的版本,请直接下载安装包来手动安装,也是十分方便的方法。在这里我安装的是 Beautiful Soup 4.3.2 Beautiful Soup 3.2.1Beautiful Soup 4.3.2 下载完成之后解压 运行下面的命令即可完成安装

1
sudo python setup.py install

然后需要安装 lxml

1
easy_install lxml
1
pip install lxml

另一个可供选择的解析器是纯Python实现的 html5lib , html5lib的解析方式与浏览器相同,可以选择下列方法来安装html5lib:

1
easy_install html5lib
1
pip install html5lib

Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,如果我们不安装它,则 Python 会使用 Python默认的解析器,lxml 解析器更加强大,速度更快,推荐安装。

<thead”>

解析器

使用方法

优势

劣势

Python标准库

BeautifulSoup(markup, “html.parser”)

  • Python的内置标准库
  • 执行速度适中
  • 文档容错能力强

  • Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差

lxml HTML 解析器

BeautifulSoup(markup, “lxml”)

  • 速度快
  • 文档容错能力强

  • 需要安装C语言库

lxml XML 解析器

BeautifulSoup(markup, [“lxml”, “xml”])BeautifulSoup(markup, “xml”)

  • 速度快
  • 唯一支持XML的解析器

  • 需要安装C语言库

html5lib

BeautifulSoup(markup, “html5lib”)

  • 最好的容错性
  • 以浏览器的方式解析文档
  • 生成HTML5格式的文档

  • 速度慢

  • 不依赖外部扩展

3. 开启Beautiful Soup 之旅

在这里先分享官方文档链接,不过内容是有些多,也不够条理,在此本文章做一下整理方便大家参考。 官方文档

4. 创建 Beautiful Soup 对象

首先必须要导入 bs4 库

1
from bs4 import BeautifulSoup

我们创建一个字符串,后面的例子我们便会用它来演示

1
2
3
4
5
6
7
8
9
10
11
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

创建 beautifulsoup 对象

1
soup = BeautifulSoup(html)

另外,我们还可以用本地 HTML 文件来创建对象,例如

1
soup = BeautifulSoup(open('index.html'))

上面这句代码便是将本地 index.html 文件打开,用它来创建 soup 对象 下面我们来打印一下 soup 对象的内容,格式化输出

1
print soup.prettify()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<html>
<head>
<title>
The Dormouse's story
</title>
</head>
<body>
<p class="title" name="dromouse">
<b>
The Dormouse's story
</b>
</p>
<p class="story">
Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1">
<!-- Elsie -->
</a>
,
<a class="sister" href="http://example.com/lacie" id="link2">
Lacie
</a>
and
<a class="sister" href="http://example.com/tillie" id="link3">
Tillie
</a>
;
and they lived at the bottom of a well.
</p>
<p class="story">
...
</p>
</body>
</html>

以上便是输出结果,格式化打印出了它的内容,这个函数经常用到,小伙伴们要记好咯。

5. 四大对象种类

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment

下面我们进行一一介绍

(1)Tag

Tag 是什么?通俗点讲就是 HTML 中的一个个标签,例如

1
<title>The Dormouse's story</title>
1
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

上面的 title a 等等 HTML 标签加上里面包括的内容就是 Tag,下面我们来感受一下怎样用 Beautiful Soup 来方便地获取 Tags 下面每一段代码中注释部分即为运行结果

1
2
print soup.title
#<title>The Dormouse's story</title>
1
2
print soup.head
#<head><title>The Dormouse's story</title></head>
1
2
print soup.a
#<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
1
2
print soup.p
#<p class="title" name="dromouse"><b>The Dormouse's story</b></p>

我们可以利用 soup加标签名轻松地获取这些标签的内容,是不是感觉比正则表达式方便多了?不过有一点是,它查找的是在所有内容中的第一个符合要求的标签,如果要查询所有的标签,我们在后面进行介绍。 我们可以验证一下这些对象的类型

1
2
print type(soup.a)
#<class 'bs4.element.Tag'>

对于 Tag,它有两个重要的属性,是 name 和 attrs,下面我们分别来感受一下 name

1
2
3
4
print soup.name
print soup.head.name
#[document]
#head

soup 对象本身比较特殊,它的 name 即为 [document],对于其他内部标签,输出的值便为标签本身的名称。 attrs

1
2
print soup.p.attrs
#{'class': ['title'], 'name': 'dromouse'}

在这里,我们把 p 标签的所有属性打印输出了出来,得到的类型是一个字典。 如果我们想要单独获取某个属性,可以这样,例如我们获取它的 class 叫什么

1
2
print soup.p['class']
#['title']

还可以这样,利用get方法,传入属性的名称,二者是等价的

1
2
print soup.p.get('class')
#['title']

我们可以对这些属性和内容等等进行修改,例如

1
2
3
soup.p['class']="newClass"
print soup.p
#<p class="newClass" name="dromouse"><b>The Dormouse's story</b></p>

还可以对这个属性进行删除,例如

1
2
3
del soup.p['class']
print soup.p
#<p name="dromouse"><b>The Dormouse's story</b></p>

不过,对于修改删除的操作,不是我们的主要用途,在此不做详细介绍了,如果有需要,请查看前面提供的官方文档

(2)NavigableString

既然我们已经得到了标签的内容,那么问题来了,我们要想获取标签内部的文字怎么办呢?很简单,用 .string 即可,例如

1
2
print soup.p.string
#The Dormouse's story

这样我们就轻松获取到了标签里面的内容,想想如果用正则表达式要多麻烦。它的类型是一个 NavigableString,翻译过来叫 可以遍历的字符串,不过我们最好还是称它英文名字吧。 来检查一下它的类型

1
2
print type(soup.p.string)
#<class 'bs4.element.NavigableString'>

(3)BeautifulSoup

BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性来感受一下

1
2
3
4
5
6
print type(soup.name)
#<type 'unicode'>
print soup.name
# [document]
print soup.attrs
#{} 空字典

(4)Comment

Comment 对象是一个特殊类型的 NavigableString 对象,其实输出的内容仍然不包括注释符号,但是如果不好好处理它,可能会对我们的文本处理造成意想不到的麻烦。 我们找一个带注释的标签

1
2
3
print soup.a
print soup.a.string
print type(soup.a.string)

运行结果如下

1
2
3
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
Elsie
<class 'bs4.element.Comment'>

a 标签里的内容实际上是注释,但是如果我们利用 .string 来输出它的内容,我们发现它已经把注释符号去掉了,所以这可能会给我们带来不必要的麻烦。 另外我们打印输出下它的类型,发现它是一个 Comment 类型,所以,我们在使用前最好做一下判断,判断代码如下

1
2
if type(soup.a.string)==bs4.element.Comment:
print soup.a.string

上面的代码中,我们首先判断了它的类型,是否为 Comment 类型,然后再进行其他操作,如打印输出。

6. 遍历文档树

(1)直接子节点

要点:.contents .children 属性

.contents tag 的 .content 属性可以将tag的子节点以列表的方式输出

1
2
print soup.head.contents 
#[<title>The Dormouse's story</title>]

输出方式为列表,我们可以用列表索引来获取它的某一个元素

1
2
print soup.head.contents[0]
#<title>The Dormouse's story</title>

.children 它返回的不是一个 list,不过我们可以通过遍历获取所有子节点。 我们打印输出 .children 看一下,可以发现它是一个 list 生成器对象

1
2
print soup.head.children
#<listiterator object at 0x7f71457f5710>

我们怎样获得里面的内容呢?很简单,遍历一下就好了,代码及结果如下

1
2
for child in  soup.body.children:
print child
1
2
3
4
5
6
7
8
9
10
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>


<p class="story">...</p>

(2)所有子孙节点

知识点:.descendants 属性

.descendants .contents 和 .children 属性仅包含tag的直接子节点,.descendants 属性可以对所有tag的子孙节点进行递归循环,和 children类似,我们也需要遍历获取其中的内容。

1
2
for child in soup.descendants:
print child

运行结果如下,可以发现,所有的节点都被打印出来了,先生最外层的 HTML标签,其次从 head 标签一个个剥离,以此类推。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
</body></html>
<head><title>The Dormouse's story</title></head>
<title>The Dormouse's story</title>
The Dormouse's story


<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
</body>


<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<b>The Dormouse's story</b>
The Dormouse's story


<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
Once upon a time there were three little sisters; and their names were

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
Elsie
,

<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
Lacie
and

<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
Tillie
;
and they lived at the bottom of a well.


<p class="story">...</p>
...

(3)节点内容

知识点:.string 属性

如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点。如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同。 通俗点说就是:如果一个标签里面没有标签了,那么 .string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容。例如

1
2
3
4
print soup.head.string
#The Dormouse's story
print soup.title.string
#The Dormouse's story

如果tag包含了多个子节点,tag就无法确定,string 方法应该调用哪个子节点的内容, .string 的输出结果是 None

1
2
print soup.html.string
# None

(4)多个内容

知识点: .strings .stripped_strings 属性

.strings 获取多个内容,不过需要遍历获取,比如下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for string in soup.strings:
print(repr(string))
# u"The Dormouse's story"
# u'\n\n'
# u"The Dormouse's story"
# u'\n\n'
# u'Once upon a time there were three little sisters; and their names were\n'
# u'Elsie'
# u',\n'
# u'Lacie'
# u' and\n'
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# u'...'
# u'\n'

.stripped_strings 输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容

1
2
3
4
5
6
7
8
9
10
11
12
for string in soup.stripped_strings:
print(repr(string))
# u"The Dormouse's story"
# u"The Dormouse's story"
# u'Once upon a time there were three little sisters; and their names were'
# u'Elsie'
# u','
# u'Lacie'
# u'and'
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'...'

(5)父节点

知识点: .parent 属性

1
2
3
p = soup.p
print p.parent.name
#body
1
2
3
content = soup.head.title.string
print content.parent.name
#title

(6)全部父节点

知识点:.parents 属性

通过元素的 .parents 属性可以递归得到元素的所有父辈节点,例如

1
2
3
content = soup.head.title.string
for parent in content.parents:
print parent.name
1
2
3
4
title
head
html
[document]

(7)兄弟节点

知识点:.next_sibling .previous_sibling 属性

兄弟节点可以理解为和本节点处在统一级的节点,.next_sibling 属性获取了该节点的下一个兄弟节点,.previous_sibling 则与之相反,如果节点不存在,则返回 None 注意:实际文档中的tag的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行

1
2
3
4
5
6
7
8
9
10
11
print soup.p.next_sibling
# 实际该处为空白
print soup.p.prev_sibling
#None 没有前一个兄弟节点,返回 None
print soup.p.next_sibling.next_sibling
#<p class="story">Once upon a time there were three little sisters; and their names were
#<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
#<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
#<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
#and they lived at the bottom of a well.</p>
#下一个节点的下一个兄弟节点是我们可以看到的节点

(8)全部兄弟节点

知识点:.next_siblings .previous_siblings 属性

通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出

1
2
3
4
5
6
7
8
for sibling in soup.a.next_siblings:
print(repr(sibling))
# u',\n'
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
# u' and\n'
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
# u'; and they lived at the bottom of a well.'
# None

(9)前后节点

知识点:.next_element .previous_element 属性

与 .next_sibling .previous_sibling 不同,它并不是针对于兄弟节点,而是在所有节点,不分层次 比如 head 节点为

1
<head><title>The Dormouse's story</title></head>

那么它的下一个节点便是 title,它是不分层次关系的

1
2
print soup.head.next_element
#<title>The Dormouse's story</title>

(10)所有前后节点

知识点:.next_elements .previous_elements 属性

通过 .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样

1
2
3
4
5
6
7
8
9
for element in last_a_tag.next_elements:
print(repr(element))
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# <p class="story">...</p>
# u'...'
# u'\n'
# None

以上是遍历文档树的基本用法。

7.搜索文档树

(1)find_all( name , attrs , recursive , text , **kwargs )

find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件 1)name 参数 name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉 A.传字符串 最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的标签

1
2
soup.find_all('b')
# [<b>The Dormouse's story</b>]
1
2
print soup.find_all('a')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

B.传正则表达式 如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以b开头的标签,这表示 和标签都应该被找到

1
2
3
4
5
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
# body
# b

C.传列表 如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和标签

1
2
3
4
5
soup.find_all(["a", "b"])
# [<b>The Dormouse's story</b>,
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

D.传 True True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点

1
2
3
4
5
6
7
8
9
10
11
for tag in soup.find_all(True):
print(tag.name)
# html
# head
# title
# body
# p
# b
# p
# a
# a

E.传方法 如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 [4] ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False 下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True:

1
2
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')

将这个方法作为参数传入 find_all() 方法,将得到所有

标签:

1
2
3
4
soup.find_all(has_class_but_no_id)
# [<p class="title"><b>The Dormouse's story</b></p>,
# <p class="story">Once upon a time there were...</p>,
# <p class="story">...</p>]

2)keyword 参数

注意:如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性

1
2
soup.find_all(id='link2')
# [<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

如果传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性

1
2
soup.find_all(href=re.compile("elsie"))
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

使用多个指定名字的参数可以同时过滤tag的多个属性

1
2
soup.find_all(href=re.compile("elsie"), id='link1')
# [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]

在这里我们想用 class 过滤,不过 class 是 python 的关键词,这怎么办?加个下划线就可以

1
2
3
4
soup.find_all("a", class_="sister")
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性

1
2
3
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>')
data_soup.find_all(data-foo="value")
# SyntaxError: keyword can't be an expression

但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag

1
2
data_soup.find_all(attrs={"data-foo": "value"})
# [<div data-foo="value">foo!</div>]

3)text 参数 通过 text 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, text 参数接受 字符串 , 正则表达式 , 列表, True

1
2
3
4
5
6
7
8
soup.find_all(text="Elsie")
# [u'Elsie']

soup.find_all(text=["Tillie", "Elsie", "Lacie"])
# [u'Elsie', u'Lacie', u'Tillie']

soup.find_all(text=re.compile("Dormouse"))
[u"The Dormouse's story", u"The Dormouse's story"]

4)limit 参数 find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果. 文档树中有3个tag符合搜索条件,但结果只返回了2个,因为我们限制了返回数量

1
2
3
soup.find_all("a", limit=2)
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

5)recursive 参数 调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False . 一段简单的文档:

1
2
3
4
5
6
7
<html>
<head>
<title>
The Dormouse's story
</title>
</head>
...

是否使用 recursive 参数的搜索结果:

1
2
3
4
5
soup.html.find_all("title")
# [<title>The Dormouse's story</title>]

soup.html.find_all("title", recursive=False)
# []

(2)find( name , attrs , recursive , text , **kwargs )

它与 find_all() 方法唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果

(3)find_parents() find_parent()

find_all() 和 find() 只搜索当前节点的所有子节点,孙子节点等. find_parents() 和 find_parent() 用来搜索当前节点的父辈节点,搜索方法与普通tag的搜索方法相同,搜索文档搜索文档包含的内容

(4)find_next_siblings() find_next_sibling()

这2个方法通过 .next_siblings 属性对当 tag 的所有后面解析的兄弟 tag 节点进行迭代, find_next_siblings() 方法返回所有符合条件的后面的兄弟节点,find_next_sibling() 只返回符合条件的后面的第一个tag节点

(5)find_previous_siblings() find_previous_sibling()

这2个方法通过 .previous_siblings 属性对当前 tag 的前面解析的兄弟 tag 节点进行迭代, find_previous_siblings() 方法返回所有符合条件的前面的兄弟节点, find_previous_sibling() 方法返回第一个符合条件的前面的兄弟节点

(6)find_all_next() find_next()

这2个方法通过 .next_elements 属性对当前 tag 的之后的 tag 和字符串进行迭代, find_all_next() 方法返回所有符合条件的节点, find_next() 方法返回第一个符合条件的节点

(7)find_all_previous() 和 find_previous()

这2个方法通过 .previous_elements 属性对当前节点前面的 tag 和字符串进行迭代, find_all_previous() 方法返回所有符合条件的节点, find_previous()方法返回第一个符合条件的节点

注:以上(2)(3)(4)(5)(6)(7)方法参数用法与 find_all() 完全相同,原理均类似,在此不再赘述。

8.CSS选择器

我们在写 CSS 时,标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list

(1)通过标签名查找

1
2
print soup.select('title') 
#[<title>The Dormouse's story</title>]
1
2
print soup.select('a')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1
2
print soup.select('b')
#[<b>The Dormouse's story</b>]

(2)通过类名查找

1
2
print soup.select('.sister')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

(3)通过 id 名查找

1
2
print soup.select('#link1')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

(4)组合查找

组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开

1
2
print soup.select('p #link1')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

直接子标签查找

1
2
print soup.select("head > title")
#[<title>The Dormouse's story</title>]

(5)属性查找

查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。

1
2
print soup.select('a[class="sister"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
1
2
print soup.select('a[href="http://example.com/elsie"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

同样,属性仍然可以与上述查找方式组合,不在同一节点的空格隔开,同一节点的不加空格

1
2
print soup.select('p a[href="http://example.com/elsie"]')
#[<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>]

以上的 select 方法返回的结果都是列表形式,可以遍历形式输出,然后用 get_text() 方法来获取它的内容。

1
2
3
4
5
6
soup = BeautifulSoup(html, 'lxml')
print type(soup.select('title'))
print soup.select('title')[0].get_text()

for title in soup.select('title'):
print title.get_text()

好,这就是另一种与 find_all 方法有异曲同工之妙的查找方法,是不是感觉很方便?

总结

本篇内容比较多,把 Beautiful Soup 的方法进行了大部分整理和总结,不过这还不算完全,仍然有 Beautiful Soup 的修改删除功能,不过这些功能用得比较少,只整理了查找提取的方法,希望对大家有帮助!小伙伴们加油! 熟练掌握了 Beautiful Soup,一定会给你带来太多方便,加油吧!

福利专区

向多人收取文件,并保存于百度盘中,适合收取作业啦,资源啦等等。 2015-03-09 23:38:06 的屏幕截图 这个比较有意思,两步就可以创建一个页面,用于接收任何人发送的文件。 为它授权百度盘账号后,就可以设定一下接收内容了,比如头部颜色、LOGO、名称,说明等等。 文件接收时间最长 30 天,收到的文件位于百度网盘 > 我的应用数据 > DzzCloud 下,然后就没有然后了。 小团队、学校、办公室等地方用于接收多人文件、作业太适合不过了,无需注册打开即可上传。 福利链接 向我传送官方地址

PHP

综述

上一节我们学习了文件的读写操作,这一节我们来看一下文件上传和下载的相关内容。

文件上传

1.PHP配置文件

首先,我们文件上传需要设定一下 php.ini 的配置文件。这是最基本的设置,如果这里设置不成功,那么代码写得再正确也没有用。基本的配置项目如下

file_uploads = on #文件上传开启 upload_max_filesize= 200M #文件上传的最大尺寸 upload_tmp_dir = c:/uploads/ #临时文件目录 post_max_size = 250M #POST时最大尺寸,必须要大于 upload_max_filesize

2.上传时注意事项

1) 文件上传操作表单提交方法必须为 post 2)文件上传时,input type 必须为 file 类型 3)文件上传的表单中,需要增加一个隐含内容,代码如下,value 的单位是 B

1
<input type="hidden" name="MAX_FILE_SIZE" value="100000000">

4)enctype=”multipart/form-data” 只有文件上传时才使用这个值,用来指定表单编码的数据方式,让服务器知道我们要传递一个文件并带有一些常规的表单信息。如下

1
2
<form action="upload.php" method="post" enctype="multipart/form-data">
</form>

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<head>
<title> File Uploads </title>
</head>
<body>
<form action="b.php" method="post" enctype="multipart/form-data">
shopname: <input type="text" name="shopname" > <br>
shopprice: <input type="text" name="price"> <br>
shopnum : <input type="text" name="num"> <br>
shoppic: <input type="file" name="pic"> <br>
<input type="submit" name="sub" value="添加商品">
</form>
</body>
</html>

文件 a.php 表单提交到了 b.php 文件,在文件 b.php 中如下

1
2
3
4
5
6
<?php 
echo "<pre>";
print_r($_POST);
print_r($_FILES);
echo "</pre>";
?>

一个是输出 POST得到的数据内容,另一个是输出获取到的文件信息。 运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Array
(
[shopname] => abc
[price] => abc
[num] => add
[sub] => 添加商品
)
Array
(
[pic] => Array
(
[name] => QPGF.dll
[type] => application/qscall-plugin
[tmp_name] => D:\wamp\tmp\phpC2C7.tmp
[error] => 0
[size] => 199224
)

)

如果不加 enctype=”multipart/form-data” 那么 print_r($_FILES) 不会有任何输出 又比如多文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>
<head>
<title> File Uploads </title>
</head>
<body>
<form action="b.php" method="post" enctype="multipart/form-data">
shopname: <input type="text" name="shopname" > <br>
shopprice: <input type="text" name="price"> <br>
shopnum : <input type="text" name="num"> <br>
shoppic: <input type="file" name="pic1"> <br>
shoppic: <input type="file" name="pic2"> <br>
shoppic: <input type="file" name="pic3"> <br>
<input type="submit" name="sub" value="添加商品">
</form>
</body>
</html>

file的name需要不同的名字,那么上面的代码输出结果为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Array
(
[pic1] => Array
(
[name] => libtcmalloc.dll
[type] => application/qscall-plugin
[tmp_name] => D:\wamp\tmp\phpE51E.tmp
[error] => 0
[size] => 178232
)

[pic2] => Array
(
[name] => libexpatw.dll
[type] => application/qscall-plugin
[tmp_name] => D:\wamp\tmp\phpE52E.tmp
[error] => 0
[size] => 130104
)

[pic3] => Array
(
[name] => AsyncTask.dll
[type] => application/qscall-plugin
[tmp_name] => D:\wamp\tmp\phpE52F.tmp
[error] => 0
[size] => 84536
)

)

还可以将name设定为一个数组,如

1
2
3
shoppic: <input type="file" name="pic[]"> <br>
shoppic: <input type="file" name="pic[]"> <br>
shoppic: <input type="file" name="pic[]"> <br>

则输出会是一个三维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Array
(
[pic] => Array
(
[name] => Array
(
[0] => libtcmalloc.dll
[1] => libexpatw.dll
[2] => QQProtect.dll
)

[type] => Array
(
[0] => application/qscall-plugin
[1] => application/qscall-plugin
[2] => application/qscall-plugin
)

[tmp_name] => Array
(
[0] => D:\wamp\tmp\phpA17D.tmp
[1] => D:\wamp\tmp\phpA17E.tmp
[2] => D:\wamp\tmp\phpA17F.tmp
)

[error] => Array
(
[0] => 0
[1] => 0
[2] => 0
)

[size] => Array
(
[0] => 178232
[1] => 130104
[2] => 387128
)

)

)

3. 文件上传后的检查

加入上传的表单中文件的name是pic,那么检查的四个方法如下: 1)使用 $_FILES[‘file’][‘error’] 检查错误 2)使用 $_FILES[‘file’][‘size’] 限制大小,单位是字节 3)使用 $_FILES[‘pic’][‘type’] 获取文件或站名,限制文件的类型 4)将上传后的文件名改名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
//step 1 使用$_FILES['pic']["error"] 检查错误

if($_FILES["pic"]["error"] > 0){
switch($_FILES["pic"]["error"]) {
case 1:
echo "上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值<br>";
break;
case 2:
echo "上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值";
break;

case 3:
echo "文件只有部分被上传";
break;

case 4:
echo "没有文件被上传";
break;

default:

echo "末知错误";

}
exit;
}

$maxsize=5000000; //50k

//step 2 使用$_FILES["pic"]["size"] 限制大小 单位字节 2M=2000000
if($_FILES["pic"]["size"] > $maxsize ) {
echo "上传的文件太大,不能超过{$maxsize}字节";
exit;
}

//step 3 使用$_FILES["pic"]["type"]或是文件的扩展名 限制类型 MIME image/gif image/png gif png jpg

/* list($dl, $xl) = explode("/", $_FILES["pic"]["type"]);

if($dl!="image"){
echo "请上传一个图片,不充许其它类型文件";
exit;
}
*/

$allowtype=array("png", "gif", "jpg", "jpeg");
$arr=explode(".", $_FILES["pic"]["name"]);
$hz=$arr[count($arr)-1];
if(!in_array($hz, $allowtype)){
echo "这是不充许的类型";
exit;
}

//step 4 将让传后的文件名改名


$filepath="./uploads/";
$randname=date("Y").date("m").date("d").date("H").date("i").date("s").rand(100, 999).".".$hz;
//将临时位置的文件移动到指定的目录上即可
if(is_uploaded_file($_FILES["pic"]["tmp_name"])){
if(move_uploaded_file($_FILES["pic"]["tmp_name"], $filepath.$randname)){
echo "上传成功";
}else{
echo "上传失败";
}
}else{
echo "不是一个上传文件";
}

以上便实现了文件上传的检测,包括错误检测,文件大小检测,文件类型检测以及文件更名等等。

文件上传类

在上面的介绍中,我们没有将文件的上传做一个封装,不过,将文件上传个功能封装成一个类的确是一个不错的选择。下面便是一个实例DEMO,让我们来感受一下吧!

1
FileUpload.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
<?php
class FileUpload {

private $filePath; //指定上传文件保存的路径
private $allowType=array('gif', 'jpg', 'png', 'jpeg'); //充许上传文件的类型
private $maxSize=1000000; //允上传文件的最大长度 1M
private $isRandName=true; //是否随机重命名, true false不随机,使用原文件名
private $originName; //源文件名称
private $tmpFileName; //临时文件名
private $fileType; //文件类型
private $fileSize; //文件大小
private $newFileName; //新文件名
private $errorNum=0; //错误号
private $errorMess=""; //用来提供错误报告



//用于对上传文件初使化
//1. 指定上传路径, 2,充许的类型, 3,限制大小, 4,是否使用随机文件名称
//让用户可以不用按位置传参数,后面参数给值不用将前几个参数也提供值
function __construct($options=array()){
foreach($options as $key=>$val){
//查看用户参数中数组的下标是否和成员属性名相同
if(!in_array($key,get_class_vars(get_class($this)))){
continue;
}
//设置成员变量
$this->setOption($key, $val);
}
}


//获得错误原因
private function getError(){
//获得错误原因
$str="上传文件<font color='red'>{$this->originName}</font>时出错:";
switch($this->errorNum){
case 4: $str .= "没有文件被上传"; break;
case 3: $str .= "文件只被部分上传"; break;
case 2: $str .= "上传文件超过了HTML表单中MAX_FILE_SIZE选项指定的值"; break;
case 1: $str .= "上传文件超过了php.ini 中upload_max_filesize选项的值"; break;
case -1: $str .= "不被充许的类型"; break;
case -2: $str .= "文件过大,上传文件不能超过{$this->maxSize}个字节"; break;
case -3: $str .= "上传失败"; break;
case -4: $str .= "建立存放上传文件目录失败,请重新指定上传目录"; break;
case -5: $str .= "必须指定上传文件的路径"; break;
default: $str .= "末知错误";
}
return $str.'<br>';
}


//用来检查文件上传路径
private function checkFilePath(){
if(empty($this->filePath)) {
$this->setOption('errorNum', -5);
return false;
}
if(!file_exists($this->filePath) || !is_writable($this->filePath)){
if(!@mkdir($this->filePath, 0755)){
$this->setOption('errorNum', -4);
return false;
}
}
return true;
}

//用来检查文件上传的大小
private function checkFileSize() {
if($this->fileSize > $this->maxSize){
$this->setOPtion('errorNum', '-2');
return false;
}else{
return true;
}
}

//用于检查文件上传类型
private function checkFileType() {
if(in_array(strtolower($this->fileType), $this->allowType)) {
return true;
}else{
$this->setOption('errorNum', -1);
return false;
}
}

//设置上传后的文件名称
private function setNewFileName(){
if($this->isRandName){
$this->setOption('newFileName', $this->proRandName());
} else {
$this->setOption('newFileName', $this->originName);
}
}


//设置随机文件名称
private function proRandName(){
$fileName=date("YmdHis").rand(100,999);
return $fileName.'.'.$this->fileType;
}

//设置成员变量
private function setOption($key, $val){
$this->$key=$val;
}

//用来上传一个文件
function uploadFile($fileField){

echo $fileField;
//默认返回值为True
$return=true;
//首先检查文件上传路径
if(!$this->checkFilePath()){
$this->errorMess=$this->getError();
return false;
}

//获得上传文件的名字
$name=$_FILES[$fileField]['name'];
//获得临时文件名
$tmp_name=$_FILES[$fileField]['tmp_name'];
//获得上传文件的大小
$size=$_FILES[$fileField]['size'];
//获得上传错误代号
$error=$_FILES[$fileField]['error'];

//如果上传的是多个文件
if(is_Array($name)){
//错误代号必须也是Array,因为一个文件对应一个错误代号
$errors=array();
//遍历检查文件
for($i=0; $i<count($name); $i++){
if($this->setFiles($name[$i], $tmp_name[$i], $size[$i], $error[$i])){
if(!$this->checkFileSize() || !$this->checkFileType()){
$errors[]=$this->getError();
$return=false;
}
}else{
$error[]=$this->getError();
$return=false;
}
if(!$return)
$this->setFiles();
}
if($return){
$fileNames=array();
for($i=0; $i<count($name); $i++){
if($this->setFiles($name[$i], $tmp_name[$i], $size[$i], $error[$i])){
$this->setNewFileName();
if(!$this->copyFile()){
$errors=$this->getError();
$return=false;
}else{
$fileNames[]=$this->newFileName;
}
}
}
//是一个数组
$this->newFileName=$fileNames;
}
//赋值错误信息
$this->errorMess=$errors;
return $return;
//如果是单个文件上传
} else {
if($this->setFiles($name, $tmp_name, $size, $error)){
if($this->checkFileSize() && $this->checkFileType()){
$this->setNewFileName();
if($this->copyFile()){
return true;
}else{
$return=false;
}
}else{
$return=false;
}
}else{
$return=false;
}

if(!$return)
$this->errorMess=$this->getError();


return $return;
}
}

//保存文件,将文件从临时路径移动到新路径
private function copyFile(){
if(!$this->errorNum){
$filePath=rtrim($this->filePath, '/').'/';
$filePath.=$this->newFileName;

if(@move_uploaded_file($this->tmpFileName, $filePath)) {
return true;
}else{
$this->setOption('errorNum', -3);
return false;
}

}else{
return false;
}
}

//设置和$_FILES有关的内容
private function setFiles($name="", $tmp_name='', $size=0, $error=0){

$this->setOption('errorNum', $error);
if($error){
return false;
}
$this->setOption('originName', $name);
$this->setOption('tmpFileName', $tmp_name);
//分割文件名,取最后一个后缀
$arrStr=explode('.', $name);
$this->setOption('fileType', strtolower($arrStr[count($arrStr)-1]));
$this->setOption('fileSize', $size);
return true;
}

//用于获取上传后文件的文件名
function getNewFileName(){
return $this->newFileName;
}

//上传如果失败,则调用这个方法,就可以查看错误报告
function getErrorMsg() {
return $this->errorMess;
}

}
1
 upload.php
1
2
3
4
5
6
7
8
9
10
11
12
<?php
require "FileUpload.class.php";
//实例化这个对象
$up=new FileUpload(array('isRandName'=>true,'allowType'=>array('txt', 'doc', 'php', 'gif'),'filePath'=>'./uploads/', 'maxSize'=>200000));
echo '<pre>';
//调用上传文件的方法
if($up->uploadFile('upload')){
print_r($up->getNewFileName());
}else{
print_r($up->getErrorMsg());
}
echo '</pre>';
1
 form.html
1
2
3
4
5
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="100000000">
<input type="file" name="upload"> <br>
<input type="submit" name="sub" value="upload file"><br>
</form>
1
 多文件上传的 form.html
1
2
3
4
5
6
7
8
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="MAX_FILE_SIZE" value="100000000">
<input type="file" name="upload[]"> <br>
<input type="file" name="upload[]"> <br>
<input type="file" name="upload[]"> <br>
<input type="file" name="upload[]"> <br>
<input type="submit" name="sub" value="upload file"><br>
</form>

利用上面的这个文件上传类,我们便可以轻松地实现文件上传,非常之便捷。

文件下载

对于浏览器无法直接打开的文件,我们一般只需要设置一下超链接就好了。比如

1
<a href="a.rar">a.rar</a>

点击超链接之后,便会弹出下载的提示框。 可是对于浏览器可以直接打开的文件,例如 1.html,2.php,3.gif 等等文件,如果仍然用这种超链接形式,那就行不通了,浏览器会直接跳转到这个页面。 我们怎样解决这个问题呢?很简单 我们首先要将超链接的文件名改为一个 php 文件,比如上面的链接就可以改为

1
<a href="a.php">logo.gif</a>

这样浏览器会去访问 a.php 文件,那么我们只需要在 a.php 文件中作相应处理即可,例如我们要下载 logo.gif 文件 我们就需要在 a.php 文件最开始设定头部信息,如下

1
2
3
4
5
6
<?php 
header("Content-Type:image/gif");
header('Content-Disposition: attachment; filename="logo.gif"');
header('Content-Length:300');
readfile("logo.gif");
?>

一般设置三个头部信息就好了 第一个是设置文件传输的类型,第二个是设置传送的内容为附件形式,文件名是 logo.gif,这里的filename 即为我们下载文件时命名的名字,而不是文件名本身。第三个是设置文件传输大小。 最后设置一下下载的是哪个文件就好了。利用 readfile 方法。 以上便是文件下载所需要的方法。 这样,文件上传和文件下载的方法就全部介绍完啦!

JavaScript

综述

本篇的主要内容来自慕课网,DOM对象,主要内容如下

  • 1 认识DOM
  • 2 getElementsByName()方法
  • 3 getElementsByTagName()方法
  • 4 区别getElementByID,getElementsByName,getElementsByTagName
  • 5 getAttribute()方法
  • 6 setAttribute()方法
  • 7 节点属性
  • 8 访问子结点childNodes
  • 9 访问子结点的第一和最后项
  • 10 访问父节点parentNode
  • 11 访问兄弟节点
  • 12 插入节点appendChild()
  • 13 插入节点insertBefore()
  • 14 删除节点removeChild()
  • 15 替换元素节点replaceChild()
  • 16 创建元素节点createElement
  • 17 创建文本节点createTextNode
  • 18 浏览器窗口可视区域大小
  • 19 网页尺寸scrollHeight
  • 20 网页尺寸offsetHeight
  • 21 网页卷去的距离与偏移量

认识DOM

文档对象模型DOM(Document Object Model)定义访问和处理HTML文档的标准方法。DOM 将HTML文档呈现为带有元素、属性和文本的树结构(节点树)。 先来看看下面代码:

将HTML代码分解为DOM节点层次图:

HTML**文档可以说由节点构成的集合,DOM节点有: 1. 元素节点:上图中、 、

等都是元素节点,即标签。 2. 文本节点:向用户展示的内容,如

  • ...
  • 中的JavaScript、DOM、CSS等文本。 3. 属性节点:元素属性,如标签的链接属性href=”http://www.imooc.com"。 节点属性: 遍历节点树: 以上图ul为例,它的父级节点body,它的子节点3个li,它的兄弟结点h2、P。 DOM操作:**

    getElementsByName()方法

    返回带有指定名称的节点对象的集合。

    语法:

    1
    document.getElementsByName(name)

    与getElementById() 方法不同的是,通过元素的 name 属性查询元素,而不是通过 id 属性。

    注意:

    1. 因为文档中的 name 属性可能不唯一,所有 getElementsByName() 方法返回的是元素的数组,而不是一个元素。

    2. 和数组类似也有length属性,可以和访问数组一样的方法来访问,从0开始。

    看看下面的代码:

    运行结果:

    getElementsByTagName()方法

    返回带有指定标签名的节点对象的集合。返回元素的顺序是它们在文档中的顺序。 语法:

    1
    getElementsByTagName(Tagname)

    说明: 1. Tagname是标签的名称,如p、a、img等标签名。 2. 和数组类似也有length属性,可以和访问数组一样的方法来访问,所以从0开始。 看看下面代码,通过getElementsByTagName()获取节点。

    区别getElementByID,getElementsByName,getElementsByTagName

    以人来举例说明,人有能标识身份的身份证,有姓名,有类别(大人、小孩、老人)等。 1. ID 是一个人的身份证号码,是唯一的。所以通过getElementById获取的是指定的一个人。 2. Name 是他的名字,可以重复。所以通过getElementsByName获取名字相同的人集合。 3. TagName可看似某类,getElementsByTagName获取相同类的人集合。如获取小孩这类人,getElementsByTagName(“小孩”)。 把上面的例子转换到HTML中,如下:

    1
    <input type="checkbox" name="hobby" id="hobby1">  音乐

    input标签就像人的类别。 name属性就像人的姓名。 id属性就像人的身份证。 方法总结如下: 注意:方法区分大小写 通过下面的例子(6个name=”hobby”的复选项,两个按钮)来区分三种方法的不同:

    1
    2
    3
    4
    5
    6
    7
    8
    <input type="checkbox" name="hobby" id="hobby1">  音乐
    <input type="checkbox" name="hobby" id="hobby2"> 登山
    <input type="checkbox" name="hobby" id="hobby3"> 游泳
    <input type="checkbox" name="hobby" id="hobby4"> 阅读
    <input type="checkbox" name="hobby" id="hobby5"> 打球
    <input type="checkbox" name="hobby" id="hobby6"> 跑步
    <input type="button" value = "全选" id="button1">
    <input type="button" value = "全不选" id="button1">

    1. document.getElementsByTagName(“input”),结果为获取所有标签为input的元素,共8个。 2. document.getElementsByName(“hobby”),结果为获取属性name=”hobby”的元素,共6个。 3. document.getElementById(“hobby6”),结果为获取属性id=”hobby6”的元素,只有一个,”跑步”这个复选项。

    getAttribute()方法

    通过元素节点的属性名称获取属性的值。 语法:

    1
    elementNode.getAttribute(name)

    说明: 1. elementNode:使用getElementById()、getElementsByTagName()等方法,获取到的元素节点。 2. name:要想查询的元素节点的属性名字 看看下面的代码,获取h1标签的属性值: 运行结果: h1标签的ID :alink h1标签的title :getAttribute()获取标签的属值

    setAttribute()方法

    setAttribute() 方法增加一个指定名称和值的新属性,或者把一个现有的属性设定为指定的值。 语法:

    1
    elementNode.setAttribute(name,value)

    说明: 1.name: 要设置的属性名。 2.value: 要设置的属性值。 注意: 1.把指定的属性设置为指定的值。如果不存在具有指定名称的属性,该方法将创建一个新属性。 2.类似于getAttribute()方法,setAttribute()方法只能通过元素节点对象调用的函数。

    节点属性

    在文档对象模型 (DOM) 中,每个节点都是一个对象。DOM 节点有三个重要的属性 : 1. nodeName : 节点的名称 2. nodeValue :节点的值 3. nodeType :节点的类型 一、nodeName 属性: 节点的名称,是只读的。 1. 元素节点的 nodeName 与标签名相同 2. 属性节点的 nodeName 是属性的名称 3. 文本节点的 nodeName 永远是 #text 4. 文档节点的 nodeName 永远是 #document 二、nodeValue 属性:节点的值 1. 元素节点的 nodeValue 是 undefined 或 null 2. 文本节点的 nodeValue 是文本自身 3. 属性节点的 nodeValue 是属性的值 三、nodeType 属性: 节点的类型,是只读的。以下常用的几种结点类型: 元素类型 节点类型 元素 1 属性 2 文本 3 注释 8 文档 9

    访问子结点childNodes

    访问选定元素节点下的所有子节点的列表,返回的值可以看作是一个数组,他具有length属性。 语法:

    1
    elementNode.childNodes

    注意: 如果选定的节点没有子节点,则该属性返回不包含节点的 NodeList。 我们来看看下面的代码: 运行结果: IE:

    1
    2
    UL子节点个数:3
    节点类型:1

    其它浏览器:

    1
    2
    UL子节点个数:7
    节点类型:3

    注意: 1. IE全系列、firefox、chrome、opera、safari兼容问题 2. 节点之间的空白符,在firefox、chrome、opera、safari浏览器是文本节点,所以IE是3,其它浏览器是7,如下图所示: 如果把代码改成这样:

    • javascript
    • jQuery
    • PHP
    运行结果:(IE和其它浏览器结果是一样的)

    1
    2
    UL子节点个数:3
    节点类型:1

    访问子结点的第一和最后项

    一、firstChild 属性返回‘childNodes’数组的第一个子节点。如果选定的节点没有子节点,则该属性返回 NULL。 语法:

    1
    node.firstChild

    说明:与elementNode.childNodes[0]是同样的效果。 二、 lastChild 属性返回‘childNodes’数组的最后一个子节点。如果选定的节点没有子节点,则该属性返回 NULL。 语法:

    1
    node.lastChild

    说明:与elementNode.childNodes[elementNode.childNodes.length-1]是同样的效果。 注意: 上一节中,我们知道Internet Explorer 会忽略节点之间生成的空白文本节点,而其它浏览器不会。我们可以通过检测节点类型,过滤子节点。 (以后章节讲解)

    访问父节点parentNode

    获取指定节点的父节点 语法:

    1
    elementNode.parentNode

    注意:父节点只能有一个。 看看下面的例子,获取 P 节点的父节点,代码如下:

    1
    2
    3
    4
    5
    6
    7
    <div id="text">
    <p id="con"> parentNode 获取指点节点的父节点</p>
    </div>
    <script type="text/javascript">
      var mynode= document.getElementById("con");
      document.write(mynode.**parentNode**.nodeName);
    </script>

    运行结果:

    1
    2
    parentNode 获取指点节点的父节点
    DIV

    访问祖节点:

    1
    elementNode.parentNode.parentNode

    看看下面的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <div id="text">  
      <p>
        parentNode      
        <span id="con"> 获取指点节点的父节点</span>
      </p>
    </div> 
    <script type="text/javascript">
      var mynode= document.getElementById("con");
      document.write(mynode.**parentNode.parentNode.**nodeName);
    </script>

    运行结果:

    1
    2
    parentNode获取指点节点的父节点
    DIV

    注意: 浏览器兼容问题,chrome、firefox等浏览器标签之间的空白也算是一个文本节点。

    访问兄弟节点

    1. nextSibling 属性可返回某个节点之后紧跟的节点(处于同一树层级中)。 语法:
    1
    nodeObject.nextSibling

    说明:如果无此节点,则该属性返回 null。 2. previousSibling 属性可返回某个节点之前紧跟的节点(处于同一树层级中)。 语法:

    1
    nodeObject.previousSibling

    说明:如果无此节点,则该属性返回 null。 注意: 两个属性获取的是节点。Internet Explorer 会忽略节点间生成的空白文本节点(例如,换行符号),而其它浏览器不会忽略。 解决问题方法: 判断节点nodeType是否为1, 如是为元素节点,跳过。 运行结果:

    1
    2
    LI = javascript
    nextsibling: LI = jquery

    插入节点appendChild()

    在指定节点的最后一个子节点列表之后添加一个新的子节点。 语法:

    1
    appendChild(newnode)

    参数: newnode:指定追加的节点。 我们来看看,div标签内创建一个新的 P 标签,代码如下: 运行结果:

    1
    2
    3
    HTML
    JavaScript
    This is a new p

    插入节点insertBefore()

    insertBefore() 方法可在已有的子节点前插入一个新的子节点。

    语法:

    insertBefore(newnode,node);

    参数:

    newnode: 要插入的新节点。

    node: 指定此节点前插入节点。

    我们在来看看下面代码,在指定节点前插入节点。

    运行结果:

    1
    2
    3
    This is a new p
    JavaScript
    HTML

    注意: otest.insertBefore(newnode,node); 也可以改为: otest.insertBefore(newnode,otest.childNodes[0]);

    删除节点removeChild()

    removeChild() 方法从子节点列表中删除某个节点。如删除成功,此方法可返回被删除的节点,如失败,则返回 NULL。 语法:

    1
    nodeObject.removeChild(node)

    参数: node :必需,指定需要删除的节点。 我们来看看下面代码,删除子点。 运行结果:

    1
    2
    HTML
    删除节点的内容: javascript

    注意: 把删除的子节点赋值给 x,这个子节点不在DOM树中,但是还存在内存中,可通过 x 操作。 如果要完全删除对象,给 x 赋 null 值,代码如下:

    替换元素节点replaceChild()

    replaceChild 实现子节点(对象)的替换。返回被替换对象的引用。 语法:

    1
    node.replaceChild (newnode,oldnew )

    参数: newnode : 必需,用于替换 oldnew 的对象。 oldnew : 必需,被 newnode 替换的对象。 我们来看看下面的代码: 效果: 将文档中的 Java 改为 JavaScript。 注意: 1. 当 oldnode 被替换时,所有与之相关的属性内容都将被移除。 2. newnode 必须先被建立。

    创建元素节点createElement

    createElement()方法可创建元素节点。此方法可返回一个 Element 对象。 语法:

    1
    document.createElement(tagName)

    参数: tagName:字符串值,这个字符串用来指明创建元素的类型。 注意:要与appendChild() 或 insertBefore()方法联合使用,将元素显示在页面中。 我们来创建一个按钮,代码如下:

    1
    2
    3
    4
    5
    6
    7
    <script type="text/javascript">
    var body = document.body;
    var input = document.createElement("input");
    input.type = "button";
    input.value = "创建一个按钮";
    body.appendChild(input);
    </script>

    效果:在HTML文档中,创建一个按钮。 我们也可以使用setAttribute来设置属性,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <script type="text/javascript">  
    var body= document.body;
    var btn = document.createElement("input");
    btn.setAttribute("type", "text");
    btn.setAttribute("name", "q");
    btn.setAttribute("value", "使用setAttribute");
    btn.setAttribute("onclick", "javascript:alert('This is a text!');");
    body.appendChild(btn);
    </script>

    效果:在HTML文档中,创建一个文本框,使用setAttribute设置属性值。 当点击这个文本框时,会弹出对话框“This is a text!”。

    创建文本节点createTextNode

    createTextNode() 方法创建新的文本节点,返回新创建的 Text 节点。 语法:

    1
    document.createTextNode(data)

    参数: data : 字符串值,可规定此节点的文本。 我们来创建一个

    元素并向其中添加一条消息,代码如下: 运行结果:

    浏览器窗口可视区域大小

    获得浏览器窗口的尺寸(浏览器的视口,不包括工具栏和滚动条)的方法:

    一、对于IE9+、Chrome、Firefox、Opera 以及 Safari:

    • window.innerHeight - 浏览器窗口的内部高度

    • window.innerWidth - 浏览器窗口的内部宽度

    二、对于 Internet Explorer 8、7、6、5:

    • document.documentElement.clientHeight表示HTML文档所在窗口的当前高度。

    • document.documentElement.clientWidth表示HTML文档所在窗口的当前宽度。

    或者

    Document对象的body属性对应HTML文档的 标签

    • document.body.clientHeight

    • document.body.clientWidth

    在不同浏览器都实用的 JavaScript 方案:

    1
    2
    3
    4
    var w= document.documentElement.clientWidth
    || document.body.clientWidth;
    var h= document.documentElement.clientHeight
    || document.body.clientHeight;

    网页尺寸scrollHeight

    scrollHeight和scrollWidth,获取网页内容高度和宽度(不包括滚动条)。 一、针对IE、Opera:

    scrollHeight 是网页内容实际高度,可以小于 clientHeight。

    二、针对NS、FF:

    scrollHeight 是网页内容高度,不过最小值是 clientHeight。也就是说网页内容实际高度小于 clientHeight 时,scrollHeight 返回 clientHeight 。

    三、浏览器兼容性

    1
    2
    3
    4
    var w=document.documentElement.scrollWidth
    || document.body.scrollWidth;
    var h=document.documentElement.scrollHeight
    || document.body.scrollHeight;

    注意:区分大小写 scrollHeight和scrollWidth还可获取Dom元素中内容实际占用的高度和宽度。

    网页尺寸offsetHeight

    offsetHeight和offsetWidth,获取网页内容高度和宽度(包括滚动条等边线,会随窗口的显示大小改变)。

    一、值

    offsetHeight = clientHeight + 滚动条 + 边框。

    二、浏览器兼容性

    1
    2
    3
    4
    var w= document.documentElement.offsetWidth
    || document.body.offsetWidth;
    var h= document.documentElement.offsetHeight
    || document.body.offsetHeight;

    网页卷去的距离与偏移量

    我们先来看看下面的图: scrollLeft:设置或获取位于给定对象左边界与窗口中目前可见内容的最左端之间的距离 ,即左边灰色的内容。 scrollTop:设置或获取位于对象最顶端与窗口中可见内容的最顶端之间的距离 ,即上边灰色的内容。 offsetLeft:获取指定对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置 。 offsetTop:获取指定对象相对于版面或由 offsetParent 属性指定的父坐标的计算顶端位置 。 注意: 1. 区分大小写 2. offsetParent:布局中设置postion属性(Relative、Absolute、fixed)的父容器,从最近的父节点开始,一层层向上找,直到HTML的body。

    来源:慕课网

    JavaScript

    综述

    本篇的主要内容来自慕课网,内置对象,主要内容如下

    • 1 window对象
    • 2 JavaScript 计时器
    • 3 计时器setInterval()
    • 4 取消计时器clearInterval()
    • 5 计时器setTimeout()
    • 6 取消计时器clearTimeout()
    • 7 History 对象
    • 8 返回前一个浏览的页面
    • 9 返回下一个浏览的页面
    • 10 返回浏览历史中的其他页面
    • 11 Location对象
    • 12 Navigator对象
    • 13 userAgent
    • 14 screen对象
    • 15 屏幕分辨率的高和宽
    • 16 屏幕可用高和宽度

    window对象

    window对象是BOM的核心,window对象指当前的浏览器窗口。 window对象方法:

    JavaScript 计时器

    在JavaScript中,我们可以在设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。 计时器类型: 一次性计时器:仅在指定的延迟时间之后触发一次。 间隔性触发计时器:每隔一定的时间间隔就触发一次。 计时器方法:

    计时器setInterval()

    在执行时,从载入页面后每隔指定的时间执行代码。 语法:

    1
    setInterval(代码,交互时间);

    参数说明: 1. 代码:要调用的函数或要执行的代码串。 2. 交互时间:周期性执行或调用表达式之间的时间间隔,以毫秒计(1s=1000ms)。 返回值: 一个可以传递给 clearInterval() 从而取消对”代码”的周期性执行的值。 调用函数格式(假设有一个clock()函数):

    1
    2
    3
    setInterval("clock()",1000)

    setInterval(clock,1000)

    我们设置一个计时器,每隔100毫秒调用clock()函数,并将时间显示出来,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!DOCTYPE HTML>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>计时器</title>
    <script type="text/javascript">
    var int=setInterval(clock, 100)
    function clock(){
    var time=new Date();
    document.getElementById("clock").value = time;
    }
    </script>
    </head>
    <body>
    <form>
    <input type="text" id="clock" size="50" />
    </form>
    </body>
    </html>

    取消计时器clearInterval()

    clearInterval() 方法可取消由 setInterval() 设置的交互时间。

    语法:

    1
    clearInterval(id_of_setInterval)

    参数说明: id_of_setInterval:由 setInterval() 返回的 ID 值。 每隔 100 毫秒调用 clock() 函数,并显示时间。当点击按钮时,停止时间,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!DOCTYPE HTML>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>计时器</title>
    <script type="text/javascript">
    function clock(){
    var time=new Date();
    document.getElementById("clock").value = time;
    }
    // 每隔100毫秒调用clock函数,并将返回值赋值给i
    var i=setInterval("clock()",100);
    </script>
    </head>
    <body>
    <form>
    <input type="text" id="clock" size="50" />
    <input type="button" value="Stop" onclick="clearInterval(i)" />
    </form>
    </body>
    </html>

    计时器setTimeout()

    setTimeout()计时器,在载入后延迟指定时间后,去执行一次表达式,仅执行一次。

    语法:

    1
    setTimeout(代码,延迟时间);

    参数说明: 1. 要调用的函数或要执行的代码串。 2. 延时时间:在执行代码前需等待的时间,以毫秒为单位(1s=1000ms)。 当我们打开网页3秒后,在弹出一个提示框,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE HTML>
    <html>
    <head>
    <script type="text/javascript">
    setTimeout("alert('Hello!')", 3000 );
    </script>
    </head>
    <body>
    </body>
    </html>

    当按钮start被点击时,setTimeout()调用函数,在5秒后弹出一个提示框。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <!DOCTYPE HTML>
    <html>
    <head>
    <script type="text/javascript">
    function tinfo(){
    var t=setTimeout("alert('Hello!')",5000);
    }
    </script>
    </head>
    <body>
    <form>
    <input type="button" value="start" onClick="tinfo()">
    </form>
    </body>
    </html>

    要创建一个运行于无穷循环中的计数器,我们需要编写一个函数来调用其自身。在下面的代码,当按钮被点击后,输入域便从0开始计数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <!DOCTYPE HTML>
    <html>
    <head>
    <script type="text/javascript">
    var num=0;
    function numCount(){
    document.getElementById('txt').value=num;
    num=num+1;
    setTimeout("numCount()",1000);
    }
    </script>
    </head>
    <body>
    <form>
    <input type="text" id="txt" />
    <input type="button" value="Start" onClick="numCount()" />
    </form>
    </body>
    </html>

    取消计时器clearTimeout()

    setTimeout()和clearTimeout()一起使用,停止计时器。

    语法:

    1
    clearTimeout(id_of_setTimeout)

    参数说明: id_of_setTimeout:由 setTimeout() 返回的 ID 值。该值标识要取消的延迟执行代码块。 下面的例子和上节的无穷循环的例子相似。唯一不同是,现在我们添加了一个 “Stop” 按钮来停止这个计数器:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <!DOCTYPE HTML>
    <html>
    <head>
    <script type="text/javascript">
    var num=0,i;
    function timedCount(){
        document.getElementById('txt').value=num;
        num=num+1;
        i=setTimeout(timedCount,1000);
      }
      setTimeout(timedCount,1000);
      function stopCount(){
      clearTimeout(i);
      }
    </script>
    </head>
    <body>
    <form>
    <input type="text" id="txt">
    <input type="button" value="Stop" onClick="stopCount()">
    </form>
    </body>
    </html>

    History 对象

    history对象记录了用户曾经浏览过的页面(URL),并可以实现浏览器前进与后退相似导航的功能。 注意:从**窗口被打开的那一刻开始记录,每个浏览器窗口、每个标签页乃至每个框架,都有自己的history对象与特定的window对象关联。 语法:**

    1
    window.history.[属性|方法]

    注意:window可以省略。 History 对象属性 History 对象方法 使用length属性,当前窗口的浏览历史总长度,代码如下:

    1
    2
    3
    4
    <script type="text/javascript">
    var HL = window.history.length;
    document.write(HL);
    </script>

    返回前一个浏览的页面

    back()方法,加载 history 列表中的前一个 URL。 语法:

    1
    window.history.back();

    比如,返回前一个浏览的页面,代码如下:

    1
    window.history.back();

    注意:等同于点击浏览器的倒退按钮。 back()相当于go(-1),代码如下:

    1
    window.history.go(-1);

    返回下一个浏览的页面

    forward()方法,加载 history 列表中的下一个 URL。 如果倒退之后,再想回到倒退之前浏览的页面,则可以使用forward()方法,代码如下:

    1
    window.history.forward();

    注意:等价点击前进按钮。 forward()相当于go(1),代码如下:

    1
    window.history.go(1);

    返回浏览历史中的其他页面

    go()方法,根据当前所处的页面,加载 history 列表中的某个具体的页面。 语法:

    1
    window.history.go(number);

    参数: 浏览器中,返回当前页面之前浏览过的第二个历史页面,代码如下:

    1
    window.history.go(-2);

    注意:和在浏览器中单击两次后退按钮操作一样。 同理,返回当前页面之后浏览过的第三个历史页面,代码如下:

    1
    window.history.go(3);

    Location对象

    location用于获取或设置窗体的URL,并且可以用于解析URL。 语法:

    1
    location.[属性|方法]

    location对象属性图示: location 对象属性: location 对象方法:

    Navigator 对象包含有关浏览器的信息,通常用于检测浏览器与操作系统的版本。 对象属性: 查看浏览器的名称和版本,代码如下:

    1
    2
    3
    4
    5
    6
    7
    <script type="text/javascript">
    var browser=navigator.appName;
    var b_version=navigator.appVersion;
    document.write("Browser name"+browser);
    document.write("<br>");
    document.write("Browser version"+b_version);
    </script>

    userAgent

    返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串) 语法

    1
    navigator.userAgent

    几种浏览的user_agent.,像360的兼容模式用的是IE、极速模式用的是chrom的内核。 使用userAgent判断使用的是什么浏览器(假设使用的是IE8浏览器),代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function validB(){ 
    var u_agent = navigator.userAgent;
    var B_name="Failed to identify the browser";
    if(u_agent.indexOf("Firefox")>-1){
    B_name="Firefox";
    }else if(u_agent.indexOf("Chrome")>-1){
    B_name="Chrome";
    }else if(u_agent.indexOf("MSIE")>-1&&u_agent.indexOf("Trident")>-1){
    B_name="IE(8-10)";
      }
    document.write("B_name:"+B_name+"<br>");
      document.write("u_agent:"+u_agent+"<br>");
    }

    运行结果:

    screen对象

    screen对象用于获取用户的屏幕信息。 语法:

    1
    window.screen.属性

    对象属性:

    屏幕分辨率的高和宽

    window.screen 对象包含有关用户屏幕的信息。 1. screen.height 返回屏幕分辨率的高 2. screen.width 返回屏幕分辨率的宽 注意: 1.单位以像素计。 2. window.screen 对象在编写时可以不使用 window 这个前缀。 我们来获取屏幕的高和宽,代码如下:

    1
    2
    3
    4
    <script type="text/javascript">
    document.write( "屏幕宽度:"+screen.width+"px<br />" );
    document.write( "屏幕高度:"+screen.height+"px<br />" );
    </script>

    屏幕可用高和宽度

    1. screen.availWidth 属性返回访问者屏幕的宽度,以像素计,减去界面特性,比如任务栏。 2. screen.availHeight 属性返回访问者屏幕的高度,以像素计,减去界面特性,比如任务栏。 注意: 不同系统的任务栏默认高度不一样,及任务栏的位置可在屏幕上下左右任何位置,所以有可能可用宽度和高度不一样。 我们来获取屏幕的可用高和宽度,代码如下:

    1
    2
    3
    4
    <script type="text/javascript">
    document.write("可用宽度:" + screen.availWidth);
    document.write("可用高度:" + screen.availHeight);
    </script>

    注意:根据屏幕的不同显示值不同。

    来源:慕课网

    JavaScript

    综述

    本篇的主要内容来自慕课网,内置对象,主要内容如下

    • 1 什么是对象
    • 2 Date 日期对象
    • 3 返回/设置年份方法
    • 4 返回星期方法
    • 5 返回/设置时间方法
    • 6 String 字符串对象
    • 7 返回指定位置的字符
    • 8 返回指定的字符串首次出现的位置
    • 9 字符串分割split()
    • 10 提取字符串substring()
    • 11 提取指定数目的字符substr()
    • 12 Math对象
    • 13 向上取整ceil()
    • 14 向下取整floor()
    • 15 四舍五入round()
    • 16 随机数 random()
    • 17 Array 数组对象
    • 18 数组连接concat()
    • 19 指定分隔符连接数组元素join()
    • 20 颠倒数组元素顺序reverse()
    • 21 选定元素slice()
    • 22 数组排序sort()

    什么是对象

    JavaScript 中的所有事物都是对象,如:字符串、数值、数组、函数等,每个对象带有属性方法对象的属性:反映该对象某些特定的性质的,如:字符串的长度、图像的长宽等; 对象的方法:能够在对象上执行的动作。例如,表单的“提交”(Submit),时间的“获取”(getYear)等; JavaScript 提供多个内建对象,比如 String、Date、Array 等等,使用对象前先定义,如下使用数组对象:

    1
    2
    3
      var objectName =new Array();//使用new关键字定义对象
    **或者**
    var objectName =[];

    访问对象属性的语法:

    1
    objectName.propertyName

    如使用 Array 对象的 length 属性来获得数组的长度:

    1
    2
    var myarray=new Array(6);//定义数组对象
    var myl=myarray.length;//访问数组长度length属性

    以上代码执行后,myl的值将是:6 访问对象的方法:

    1
    objectName.methodName()

    如使用string 对象的 toUpperCase() 方法来将文本转换为大写:

    1
    2
    var mystr="Hello world!";//创建一个字符串
    var request=mystr.toUpperCase(); //使用字符串对象方法

    以上代码执行后,request的值是:HELLO WORLD!

    Date 日期对象

    日期对象可以储存任意一个日期,并且可以精确到毫秒数(1/1000 秒)。 定义一个时间对象 :

    1
    var Udate=new Date();

    注意**:**使用关键字new,Date()的首字母必须大写。

    使 Udate 成为日期对象,并且已有初始值:当前时间(当前电脑系统时间)

    如果要自定义初始值,可以用以下方法:

    1
    2
    var d = new Date(2012, 10, 1);  //2012年10月1日
    var d = new Date('Oct 1, 2012'); //2012年10月1日

    我们最好使用下面介绍的“方法”来严格定义时间。

    访问方法语法:“<日期对象>.<方法>”

    Date对象中处理时间和日期的常用方法:

    返回/设置年份方法

    get/setFullYear() 返回/设置年份,用四位数表示。

    1
    2
    3
    4
    5
    var mydate=new Date();//当前时间2014年3月6日
    document.write(mydate+"<br>");//输出当前时间
    document.write(mydate.getFullYear()+"<br>");//输出当前年份
    mydate.setFullYear(81); //设置年份
    document.write(mydate+"<br>"); //输出年份被设定为 0081年。

    注意:不同浏览器, mydate.setFullYear(81)结果不同,年份被设定为 0081或81两种情况。 结果:

    1
    2
    3
    Thu Mar 06 2014 10:57:47 GMT+0800
    2014
    Thu Mar 06 0081 10:57:47 GMT+0800

    注意: 1.结果格式依次为:星期、月、日、年、时、分、秒、时区。(火狐浏览器) 2. 不同浏览器,时间格式有差异。

    返回星期方法

    getDay() 返回星期,返回的是0-6的数字,0 表示星期天。如果要返回相对应“星期”,通过数组完成,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    <script type="text/javascript">
    var mydate=new Date();//定义日期对象
    var weekday=["星期日","星期一","星期二","星期三","星期四","星期五","星期六"];
    //定义数组对象,给每个数组项赋值
    var mynum=mydate.getDay();//返回值存储在变量mynum中
    document.write(mydate.getDay());//输出getDay()获取值
    document.write("今天是:"+ weekday[mynum]);//输出星期几
    </script>

    注意:以上代码是在2014年3月7日,星期五运行。

    结果:

    5

    今天是:星期五

    返回/设置时间方法

    get/setTime() 返回/设置时间,单位毫秒数,计算从 1970 年 1 月 1 日零时到日期对象所指的日期的毫秒数。

    如果将目前日期对象的时间推迟1小时,代码如下:

    1
    2
    3
    4
    5
    6
    <script type="text/javascript">
    var mydate=new Date();
    document.write("当前时间:"+mydate+"<br>");
    mydate.setTime(mydate.getTime() + 60 * 60 * 1000);
    document.write("推迟一小时时间:" + mydate);
    </script>

    结果:

    当前时间:Thu Mar 6 11:46:27 UTC+0800 2014

    推迟一小时时间:Thu Mar 6 12:46:27 UTC+0800 2014

    注意:1. 一小时 60 分,一分 60 秒,一秒 1000 毫秒

      2. 时间推迟 1 小时,就是: “x.setTime(x.getTime() + 60 * 60 * 1000);”
    

    String 字符串对象

    在之前的学习中已经使用字符串对象了,定义字符串的方法就是直接赋值。比如:

    1
    var mystr = "I love JavaScript!"

    定义mystr字符串后,我们就可以访问它的属性和方法。

    访问字符串对象的属性length:

    stringObject.length; 返回该字符串的长度。

    1
    2
    var mystr="Hello World!";
    var myl=mystr.`length`;

    以上代码执行后,myl 的值将是:12 访问字符串对象的方法: 使用 String 对象的 toUpperCase() 方法来将字符串小写字母转换为大写:

    1
    2
    var mystr="Hello world!";
    var mynum=mystr.toUpperCase();
    1
    以上代码执行后,mynum 的值是:HELLO WORLD!

    返回指定位置的字符

    charAt() 方法可返回指定位置的字符。返回的字符是长度为 1 的字符串。

    语法:

    1
    stringObject.charAt(index)

    参数说明:

    注意**:**1.字符串中第一个字符的下标是 0。最后一个字符的下标为字符串长度减一(string.length-1)。

    2.如果参数 index 不在 0 与 string.length-1 之间,该方法将返回一个空字符串。

    如:在字符串 “I love JavaScript!” 中,返回位置2的字符:

    1
    2
    3
    4
    <script type="text/javascript">
    var mystr="I love JavaScript!"
    document.write(mystr.charAt(2));
    </script>

    注意:一个空格也算一个字符。

    以上代码的运行结果:

    1
    l

    返回指定的字符串首次出现的位置

    indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。

    语法

    1
    stringObject.indexOf(substring, startpos)

    参数说明:

    说明:

    1.该方法将从头到尾地检索字符串 stringObject,看它是否含有子串 substring。

    2.可选参数,从stringObject的startpos位置开始查找substring,如果没有此参数将从stringObject的开始位置查找。

    3.如果找到一个 substring,则返回 substring 的第一次出现的位置。stringObject 中的字符位置是从 0 开始的。

    注意:1.indexOf() 方法区分大小写。

    2.如果要检索的字符串值没有出现,则该方法返回 -1。

    例如: 对 “I love JavaScript!” 字符串内进行不同的检索:

    1
    2
    3
    4
    5
    6
    <script type="text/javascript">
    var str="I love JavaScript!"
    document.write(str.indexOf("I") + "<br />");
    document.write(str.indexOf("v") + "<br />");
    document.write(str.indexOf("v",8));
    </script>

    以上代码的输出:

    1
    2
    3
    0
    4
    9

    字符串分割split()

    知识讲解: split() 方法将字符串分割为字符串数组,并返回此数组。

    语法:

    1
    stringObject.split(separator,limit)

    参数说明:

    注意:如果把空字符串 (“”) 用作 separator,那么 stringObject 中的每个字符之间都会被分割。 我们将按照不同的方式来分割字符串: 使用指定符号分割字符串,代码如下:

    1
    2
    3
    var mystr = "www.imooc.com";
    document.write(mystr.split(".")+"<br>");
    document.write(mystr.split(".", 2)+"<br>");

    运行结果:

    1
    2
    www,imooc,com
    www,imooc

    将字符串分割为字符,代码如下:

    1
    2
    document.write(mystr.split("")+"<br>");
    document.write(mystr.split("", 5));

    运行结果:

    1
    2
    w,w,w,.,i,m,o,o,c,.,c,o,m
    w,w,w,.,i

    提取字符串substring()

    substring() 方法用于提取字符串中介于两个指定下标之间的字符。 语法:

    1
    stringObject.substring(starPos,stopPos)

    参数说明: 注意: 1. 返回的内容是从 start开始(包含start位置的字符)到 stop-1 处的所有字符,其长度为 stop 减start。 2. 如果参数 start 与 stop 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)。 3. 如果 start 比 stop 大,那么该方法在提取子串之前会先交换这两个参数。 使用 substring() 从字符串中提取字符串,代码如下:

    1
    2
    3
    4
    5
    <script type="text/javascript">
      var mystr="I love JavaScript";
      document.write(mystr.substring(7));
      document.write(mystr.substring(2,6));
    </script>

    运行结果**:**

    1
    2
    JavaScript
    love

    提取指定数目的字符substr()

    substr() 方法从字符串中提取从 startPos位置开始的指定数目的字符串。 语法:

    1
    stringObject.substr(startPos,length)

    参数说明: 注意:如果参数startPos是负数,从字符串的尾部开始算起的位置。也就是说,-1 指字符串中最后一个字符,-2 指倒数第二个字符,以此类推。 如果startPos为负数且绝对值大于字符串长度,startPos为0。 使用 substr() 从字符串中提取一些字符,代码如下:

    1
    2
    3
    4
    5
    <script type="text/javascript">
    var mystr="I love JavaScript!";
    document.write(mystr.substr(7));
    document.write(mystr.substr(2,4));
    </script>

    运行结果:

    1
    2
    JavaScript!
    love

    Math对象

    Math对象,提供对数据的数学计算。 使用 Math 的属性和方法,代码如下:

    1
    2
    3
    4
    5
    6
    <script type="text/javascript">
    var mypi=Math.PI;
    var myabs=Math.abs(-15);
    document.write(mypi);
    document.write(myabs);
    </script>

    运行结果**:**

    1
    2
    3.141592653589793
    15

    注意:Math 对象是一个固有的对象,无需创建它,直接把 Math 作为对象使用就可以调用其所有属性和方法。这是它与Date,String对象的区别。 Math 对象属性 Math 对象方法 以上方法不做全部讲解,只讲解部分方法。

    向上取整ceil()

    ceil() 方法可对一个数进行向上取整。 语法:

    1
    Math.ceil(x)

    参数说明: 注意:它返回的是大于或等于x,并且与x最接近的整数。 我们将把 ceil() 方法运用到不同的数字上,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    <script type="text/javascript">
    document.write(Math.ceil(0.8) + "<br />")
    document.write(Math.ceil(6.3) + "<br />")
    document.write(Math.ceil(5) + "<br />")
    document.write(Math.ceil(3.5) + "<br />")
    document.write(Math.ceil(-5.1) + "<br />")
    document.write(Math.ceil(-5.9))
    </script>

    运行结果:

    1
    2
    3
    4
    5
    6
    1
    7
    5
    4
    -5
    -5

    向下取整floor()

    floor() 方法可对一个数进行向下取整。 语法:

    1
    Math.floor(x)

    参数说明: 注意:返回的是小于或等于x,并且与x最接近的整数。 我们将在不同的数字上使用 floor() 方法,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    <script type="text/javascript">
    document.write(Math.floor(0.8)+ "<br>")
    document.write(Math.floor(6.3)+ "<br>")
    document.write(Math.floor(5)+ "<br>")
    document.write(Math.floor(3.5)+ "<br>")
    document.write(Math.floor(-5.1)+ "<br>")
    document.write(Math.floor(-5.9))
    </script>

    运行结果:

    1
    2
    3
    4
    5
    6
    0
    6
    5
    3
    -6
    -6

    四舍五入round()

    round() 方法可把一个数字四舍五入为最接近的整数。 语法:

    1
    Math.round(x)

    参数说明: 注意: 1. 返回与 x 最接近的整数。 2. 对于 0.5,该方法将进行上舍入。(5.5 将舍入为 6) 3. 如果 x 与两侧整数同等接近,则结果接近 +∞方向的数字值 。(如 -5.5 将舍入为 -5; -5.52 将舍入为 -6),如下图: 把不同的数舍入为最接近的整数,代码如下:

    1
    2
    3
    4
    5
    6
    7
    <script type="text/javascript">
    document.write(`Math.round(1.6)`+ "<br>");
    document.write(`Math.round(2.5)`+ "<br>");
    document.write(`Math.round(0.49)`+ "<br>");
    document.write(`Math.round(-6.4)`+ "<br>");
    document.write(`Math.round(-6.6)`);
    </script>

    运行结果:

    1
    2
    3
    4
    5
    2
    3
    0
    -6
    -7

    随机数 random()

    1
    random() 方法可返回介于 0 ~ 1(大于或等于 0 但小于 1 )之间的一个随机数。

    语法:

    1
    Math.random();

    注意:返回一个大于或等于 0 但小于 1 的符号为正的数字值。 我们取得介于 0 到 1 之间的一个随机数,代码如下:

    1
    2
    3
    <script type="text/javascript">
    document.write(Math.random());
    </script>

    运行结果:

    1
    0.190305486195328

    注意**:**因为是随机数,所以每次运行结果不一样,但是0 ~ 1的数值。

    获得0 ~ 10之间的随机数,代码如下:

    1
    2
    3
    <script type="text/javascript">
    document.write((Math.random())*10);
    </script>

    运行结果:

    1
    8.72153625893887

    Array 数组对象

    数组对象是一个对象的集合,里边的对象可以是不同类型的。数组的每一个成员对象都有一个“下标”,用来表示它在数组中的位置,是从零开始的 数组定义的方法: 1. 定义了一个空数组:

    1
    var  数组名= new Array();

    2. 定义时指定有n个空元素的数组:

    1
    var 数组名 =new Array(n);

    3.定义数组的时候,直接初始化数据:

    1
    var  数组名 = [<元素1>, <元素2>, <元素3>...];

    我们定义myArray数组,并赋值,代码如下:

    1
    var myArray = [2, 8, 6];

    说明:定义了一个数组 myArray,里边的元素是:myArray[0] = 2; myArray[1] = 8; myArray[2] = 6。 数组元素使用:

    1
    数组名[下标] = 值;

    注意: 数组的下标用方括号括起来,从0开始。

    数组属性:

    length 用法:<数组对象>.length;返回:数组的长度,即数组里有多少个元素。它等于数组里最后一个元素的下标加一。 数组方法:

    数组连接concat()

    concat() 方法用于连接两个或多个数组。此方法返回一个新数组,不改变原来的数组。 语法

    1
    arrayObject.concat(array1,array2,...,arrayN)

    参数说明:

    注意: 该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。

    我们创建一个数组,将把 concat() 中的参数连接到数组 myarr 中,代码如下:

    1
    2
    3
    4
    5
    6
    7
    <script type="text/javascript">
      var mya = new Array(3);
      mya[0] = "1";
      mya[1] = "2";
      mya[2] = "3";
      document.write(mya.concat(4,5)+"<br>");
      document.write(mya); </script>

    运行结果:

    1
    2
    1,2,3,4,5
    1,2,3

    我们创建了三个数组,然后使用 concat() 把它们连接起来,代码如下:

    1
    2
    3
    4
    5
    6
    7
    <script type="text/javascript">
    var mya1= new Array("hello!")
      var mya2= new Array("I","love");
      var mya3= new Array("JavaScript","!");
      var mya4=mya1.concat(mya2,mya3);
      document.write(mya4);
    </script>

    运行结果:

    1
    hello!,I,love,JavaScript,!

    指定分隔符连接数组元素join()

    join()方法用于把数组中的所有元素放入一个字符串。元素是通过指定的分隔符进行分隔的。 语法:

    1
    arrayObject.join(分隔符)

    参数说明:

    注意:返回一个字符串,该字符串把数组中的各个元素串起来,用<分隔符>置于元素与元素之间。这个方法不影响数组原本的内容。 我们使用join()方法,将数组的所有元素放入一个字符串中,代码如下:

    1
    2
    3
    4
    5
    6
    7
    <script type="text/javascript">
    var myarr = new Array(3);
    myarr[0] = "I";
    myarr[1] = "love";
    myarr[2] = "JavaScript";
    document.write(myarr.join());
    </script>

    运行结果:

    1
    I,love,JavaScript

    我们将使用分隔符来分隔数组中的元素,代码如下:

    1
    2
    3
    4
    5
    6
    7
    <script type="text/javascript">
    var myarr = new Array(3)
    myarr[0] = "I";
    myarr[1] = "love";
    myarr[2] = "JavaScript";
    document.write(myarr.join("."));
    </script>

    运行结果:

    1
    I.love.JavaScript

    颠倒数组元素顺序reverse()

    reverse() 方法用于颠倒数组中元素的顺序。

    语法:

    1
    arrayObject.reverse()

    注意:该方法会改变原来的数组,而不会创建新的数组。 定义数组myarr并赋值,然后颠倒其元素的顺序:

    1
    2
    3
    4
    5
    6
    7
    8
    <script type="text/javascript">
    var myarr = new Array(3)
    myarr[0] = "1"
    myarr[1] = "2"
    myarr[2] = "3"
    document.write(myarr + "<br />")
    document.write(my`arr.reverse()`)
    </script>

    运行结果:

    1
    2
    1,2,3
    3,2,1

    选定元素slice()

    slice() 方法可从已有的数组中返回选定的元素。 语法

    1
    arrayObject.slice(start,end)

    参数说明: 1.返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。 2. 该方法并不会修改数组,而是返回一个子数组。 注意: 1. 可使用负值从数组的尾部选取元素。 2.如果 end 未被规定,那么 slice() 方法会选取从 start 到数组结尾的所有元素。 3. String.slice() 与 Array.slice() 相似。 我们将创建一个新数组,然后从其中选取的元素,代码如下:

    1
    2
    3
    4
    5
    6
    <script type="text/javascript">
    var myarr = new Array(1,2,3,4,5,6);
    document.write(myarr + "<br>");
    document.write(myarr.slice(2,4) + "<br>");
    document.write(myarr);
    </script>

    运行结果:

    1
    2
    3
    1,2,3,4,5,6
    3,4
    1,2,3,4,5,6

    数组排序sort()

    sort()方法使数组中的元素按照一定的顺序排列。

    语法:

    1
    arrayObject.sort(方法函数)

    参数说明:

    1.如果不指定<方法函数>,则按unicode码顺序排列。

    2.如果指定<方法函数>,则按<方法函数>所指定的排序方法排序。

    1
    myArray.sort(sortMethod);

    使用sort()将数组进行排序,代码如下:

    1
    2
    3
    4
    5
    6
    <script type="text/javascript">
    var myarr1 = new Array("Hello","John","love","JavaScript");
    var myarr2 = new Array("80","16","50","6","100","1");
    document.write(myarr1.sort()+"<br>");
    document.write(myarr2.sort());
    </script>

    运行结果:

    1
    2
    Hello,JavaScript,John,love
    1,100,16,50,6,80

    注意:上面的代码没有按照数值的大小对数字进行排序。 如要实现这一点,就必须使用一个排序函数,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <script type="text/javascript">
    function sortNum(a,b) {
    return a - b;
    //升序,如降序,把“a - b”该成“b - a”
    }
    var myarr = new Array("80","16","50","6","100","1");
    document.write(myarr + "<br>");
    document.write(myarr.sort(sortNum));
    </script>

    运行结果:

    1
    2
    80,16,50,6,100,1
    1,6,16,50,80,100

    来源:慕课网

    JavaScript

    综述

    本篇的主要内容来自慕课网,事件响应与网页交互,主要内容如下

    • 1 什么是事件
    • 2 鼠标单击事件( onclick )
    • 3 鼠标经过事件(onmouseover)
    • 4 鼠标移开事件(onmouseout)
    • 5 光标聚焦事件(onfocus)
    • 6 失焦事件(onblur)
    • 7 内容选中事件(onselect)
    • 8 文本框内容改变事件(onchange)
    • 9 加载事件(onload)
    • 10 卸载事件(onunload)

    什么是事件

    JavaScript 创建动态页面。事件是可以被 JavaScript 侦测到的行为。 网页中的每个元素都可以产生某些可以触发 JavaScript 函数或程序的事件。

    比如说,当用户单击按钮或者提交表单数据时,就发生一个鼠标单击(onclick)事件,需要浏览器做出处理,返回给用户一个结果。

    主要事件表:

    鼠标单击事件( onclick )

    onclick是鼠标单击事件,当在网页上单击鼠标时,就会发生该事件。同时onclick事件调用的程序块就会被执行,通常与按钮一起使用。 比如,我们单击按钮时,触发 onclick 事件,并调用两个数和的函数add2()。代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <html>
    <head>
      <script type="text/javascript">
      function add2(){
      var numa,numb,sum;
      numa=6;
      numb=8;
      sum=numa+numb;
      document.write("两数和为:"+sum); }
      </script>
    </head>
    <body>
    <form>
    <input name="button" type="button" value="点击提交" onclick="add2()" />
    </form>
    </body>
    </html>

    鼠标经过事件(onmouseover)

    鼠标经过事件,当鼠标移到一个对象上时,该对象就触发onmouseover事件,并执行onmouseover事件调用的程序。 现实鼠标经过”确定”按钮时,触发onmouseover事件,调用函数info(),弹出消息框,代码如下: 运行结果:

    鼠标移开事件(onmouseout)

    鼠标移开事件,当鼠标移开当前对象时,执行onmouseout调用的程序。

    当把鼠标移动到”登录”按钮上,然后再移开时,触发onmouseout事件,调用函数message(),代码如下:

    运行结果:

    光标聚焦事件(onfocus)

    当网页中的对象获得聚点时,执行onfocus调用的程序就会被执行。 如下代码, 当将光标移到文本框内时,即焦点在文本框内,触发onfocus 事件,并调用函数message()。 运行结果:

    失焦事件(onblur)

    onblur事件与onfocus是相对事件,当光标离开当前获得聚焦对象的时候,触发onblur事件,同时执行被调用的程序。 如下代码, 网页中有用户和密码两个文本框。当前光标在用户文本框内时(即焦点在文本框),在光标离开该文本框后(即失焦时),触发onblur事件,并调用函数message()。 运行结果:

    内容选中事件(onselect)

    选中事件,当文本框或者文本域中的文字被选中时,触发onselect事件,同时调用的程序就会被执行。 如下代码,当选中用户文本框内的文字时,触发onselect 事件,并调用函数message()。 运行结果:

    文本框内容改变事件(onchange)

    通过改变文本框的内容来触发onchange事件,同时执行被调用的程序。 如下代码,当用户将文本框内的文字改变后,弹出对话框“您改变了文本内容!”。 运行结果:

    加载事件(onload)

    事件会在页面加载完成后立即发生,同时执行被调用的程序。 注意:1. 加载页面时,触发onload事件,事件写在 标签内。 2. 此节的加载页面,可理解为打开一个新页面时。 如下代码,当加载一个新页面时,弹出对话框“加载中,请稍等…”。 运行结果:

    卸载事件(onunload)

    当用户退出页面时(页面关闭、页面刷新等),触发onUnload事件,同时执行被调用的程序。

    注意:不同浏览器对onunload事件支持不同。

    如下代码,当退出页面时,弹出对话框“您确定离开该网页吗?”。

    运行结果:(IE浏览器)

    来源:慕课网

    个人随笔

    话说书桌比较乱的人比较有创造力?很不幸我的桌面我好像就是这么一个人呐,哈哈哈我开玩笑的~ 一直以来养成了一个定期清理书桌的习惯,在不到那个清理的时间,我会随意摆放自己想要摆放的东西,有时候,我的桌面比较乱也是正常的。这不,今天周六了嘛,整理一下东西吧。三下五除二,桌面回归了它正常的模样。又心想干脆连橱柜一起整理了吧,不差那回事。 橱柜的东西比较多,应该是放假前的时候堆成那个样子了吧,各种衣服窝在一起,随后我找出自己的大皮箱,一点点地整理衣服。整理的时候发现,有多少衣服是我曾经穿过一个冬季或夏季,甚至只穿过一次之后,就将它束之高阁,打入冷宫,再也不去理会。仔细回想起来,这还是我大一的时候的衣服吧,一件件牛仔裤,一件件曾经以为很时尚很Fashion的衬衫、卫衣,现在我却一概不去理会。 上学期,为了参加一个比赛,爸妈给我买了第一套正装,还有领带,皮鞋,还有那亮闪闪的皮带。穿过一次之后,就觉得这样的行头的确不错。比赛回来,我便买了一件比较成熟的衬衫,还有一条黑色长裤,穿上,把衬衫那么一扎,皮带扎在最外面,顿时觉得太有范。后来,不知不觉,我似乎是穿这样的衣服上瘾了么?过年,又入手了双皮鞋,一件酒红色外套。这下,彻底就和之前的打扮相比,像是变了个人。 下午整理衣服,每每整理到一件,我都会回忆起曾经穿着这一身衣服做了什么,去了哪些地方。曾经的生活,现在想想,是那么地美好和自由。虽说现在生活也不差,但似乎觉得,自己失去了什么;也或者,将来的日子,我又会失去什么。是啊,现在都大三了,也不小了,将来的生活也再不会那么无忧无虑,什么事也不用操心。等将来的日子,为了工作,为了应酬,为了会议,自己可能会多么奔波劳累也说不定,那时候,想回归现在的生活,已经是不可能的事了吧! 至少,现在的我,还算年轻,我还有资本去做自己想做的事。等以后自己独立了,天天西装革履,衬衣领带,再想找回曾经的样子,肯定是已经做不到了。 最后,我换了身行头,刮了刮胡子,再换回这几年的装扮,让自己年轻一把~ 珍惜年轻时候的时光吧~

    崔庆才

    2015/3/7

    随笔

    PHP

    PHP中的文件处理也是一个相当重要的模块,这一篇的主要内容就是PHP中文件系统的简介。

    文件系统用途

    1. 项目处理都离不开文件处理 2. 可以用文件长时间保存数据 3. 建立缓存,在服务器中进行文件操作

    文件系统函数用法详述

    1.基本的判断函数

    is_dir — 判断给定文件名是否是一个目录 is_file — 判断给定文件名是否为一个文件 is_executable — 判断给定文件名是否可执行 is_link — 判断给定文件名是否为一个符号连接 is_readable — 判断给定文件名是否可读 is_uploaded_file — 判断文件是否是通过 HTTP POST 上传的 is_writable — 判断给定的文件名是否可写 is_writeable — is_writable 的别名

    2.文件相关信息获取

    file_exists — 检查文件或目录是否存在 fileatime — 取得文件的上次访问时间 filectime — 取得文件的 inode 修改时间 filegroup — 取得文件的组 fileinode — 取得文件的 inode filemtime — 取得文件修改时间 fileowner — 取得文件的所有者 fileperms — 取得文件的权限 filesize — 取得文件大小 filetype — 取得文件类型

    下面我们写一个例子,传入文件名,打印它的详细信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
       function getFileInfo($filename){
    if(!file_exists($filename)){
    echo '文件'.($filename).'不存在';
    return;
    }

    if(is_file($filename)){
    echo $filename.'是一个文件';
    }

    if(is_dir($filename)){
    echo $filename.'是一个目录';
    }

    if(is_executable($filename)){
    echo $filename.'是可执行文件';
    }else{
    echo $filename.'不是可执行文件';
    }

    if(is_readable($filename)){
    echo $filename.'是可读的';
    }else{
    echo $filename.'不是可读的';
    }

    if(is_writable($filename)){
    echo $filename.'是可写入的';
    }else{
    echo $filename.'不是可写入的';
    }

    echo '文件'.$filename.'的大小是'.getFileSize(filesize($filename)).'';
    echo '文件'.$filename.'的类型是'.filetype($filename).'';
    echo '文件'.$filename.'的所有者是'.fileowner($filename).'';
    echo '文件'.$filename.'的最后访问时间为'.getTime(fileatime($filename)).'';
    echo '文件'.$filename.'的inode是'.fileinode($filename).'';
    echo '文件'.$filename.'的修改时间是'.getTime(filemtime($filename)).'';
    echo '文件'.$filename.'的权限是'.fileperms($filename).'';
    }

    function getTime($time){
    return date('Y-m-d H:i:s',$time);
    }

    function getFileSize($size){
    $dw = 'B';
    if($size>=pow(2,40)){
    $size=round($size/pow(2,40),2);
    $dw = 'PB';
    }else if($size>=pow(2,30)){
    $size=round($size/pow(2,30),2);
    $dw = 'TB';
    }else if($size>=pow(2,20)){
    $size=round($size/pow(2,20),2);
    $dw = 'GB';
    }else if($size>=pow(2,10)){
    $size=round($size/pow(2,10),2);
    $dw = 'MB';
    }
    return $size.$dw;
    }
    getFileInfo('1.php');

    运行结果

    1.php是一个文件 1.php不是可执行文件 1.php是可读的 1.php不是可写入的 文件1.php的大小是2MB 文件1.php的类型是file 文件1.php的所有者是1000 文件1.php的最后访问时间为2015-03-04 12:58:33 文件1.php的inode是536185 文件1.php的修改时间是2015-03-04 12:58:32 文件1.php的权限是33204

    3.文件路径相关函数

    相对路径:相对于当前目录的上级和下级目录

    . 当前目录 .. 上一级目录

    路径分隔符号

    linux/Unix “/“ windows “\“ 不管是什么操作系统PHP的目录分割符号都支技 / (Linux)

    绝对路径:可以指的操作系统的根,也可以指的是存放网站的文档根目录

    如果是在服务器中执行(通过PHP文件处理函数执行)路径 则 “根”指的就是操作系统的根 如果程序是下载的客户端,再访问服务器中的文件时,只有通过Apache访问,“根”也就指的是文档根目录

    三个相关函数

    basename — 返回路径中的文件名部分 dirname — 返回路径中的目录部分 pathinfo — 返回文件路径的信息

    例如下面的例子

    1
    2
    3
    4
    5
    6
    7
    8
       $url1="./aaa/bbb/index.php";
    $url2="../www/yyy/login.rar";
    $url3="c:/appserv/www/demo.html";
    $url4="http://localhost/yyy/www.gif";
    echo basename($url1);
    echo basename($url2);
    echo basename($url3);
    echo basename($url4);

    运行结果

    index.php login.rar demo.html www.gif

    可以看出,basename这个函数返回的是文件的名,也就是最后一个项目。 下面我们看一下dirname的用法

    1
    2
    3
    4
    5
    6
    7
    8
       $url1="./aaa/bbb/index.php";
    $url2="../www/yyy/login.rar";
    $url3="c:/appserv/www/demo.html";
    $url4="http://localhost/yyy/www.gif";
    echo dirname(dirname($url1));
    echo dirname($url2);
    echo dirname($url3);
    echo dirname($url4);

    运行结果

    ./aaa ../www/yyy c:/appserv/www http://localhost/yyy

    可以发现,dirname这个函数可以多层嵌套使用,返回的就是它所在的路径,即除了最后一项之外所有的项。 另外 pathinfo的以上所有信息都可以获取到,另外还包括了文件名和扩展名 比如下面的结果

    Array ( [dirname] => ../www/yyy [basename] => login.rar [extension] => rar [filename] => login )

    4. 文件的创建删除修改

    touch — 创建一个文件 unlink — 删除文件 rename — 重命名一个文件或目录 copy — 拷贝文件

    例如下面的例子

    1
    2
    3
    4
    5
    touch("./php.apahce"); //创建文件
    unlink("C:/AppServ/www/xsphp/apache.php"); //删除文件
    rename("./test.txt", "d:/test2.txt"); //重命名文件
    copy("cache.txt", "./cache5.txt"); //复制文件
    chmod("a.txt",755); //设置文件权限

    权限相关内容

    rwx 表这个文件的拥有者 r读 w写 x执行 rwx 表这个文件的拥有者所在的组 r读 w写 x执行 rwx 其它用户对这个为文件的权限 r读 w写 x执行

    文件读写

    1. file_get_contents(string)

    传入文件名,直接得到文件中的文本信息,返回的内容即为文件中的文本。 例如

    1
    2
    3
    4
    <?php
    $str = file_get_contents("1.txt");
    echo $str;
    ?>

    则直接打开了 1.txt 文件中的内容,并返回文件中的文本信息。 如果文件不存在,那么会提示

    Warning: file_get_contents(2.txt): failed to open stream: No such file or directory

    同样,文件还可以是远程文件,例如,参数传入 http://www.qq.com 即可以呈现腾讯网的首页内容。 缺点:不能读取指定部分的内容,一次性全部读取。

    2. file_put_contents(filename,content)

    写入文件,filename是写入文件的文件名,content是写入内容,返回值是成功写入的字符长度。

    1
    2
    3
    <?php 
    echo file_put_contents("2.txt",'abcd');
    ?>

    2.txt 文件如果不存在,那么则会创建这个文件并写入 abcd 这个字符串,返回 4 ,为字符串的长度。 如果文件存在,则会将文件清空,然后写入字符串,返回写入长度。 缺点:不能以追加的方式写入文件。

    3.file(filename)

    file是直接打开某一个文件,返回的结果是一个数组,每一行是数组的一个元素。也就是说,获取行数只需要输出数组的大小即可。例如

    1
    2
    3
    4
    5
    <?php 
    $str = file("1.txt");
    var_dump($str);
    echo count($str);
    ?>

    即可得到数组形式的行内容,而且输出了行数。 缺点:不能读取指定部分的内容。

    4.fopen(filename,mode)

    filename是文件名,可以是路径加名,也可以是远程服务器文件。 mode是打开文件的方式

    r,以只读模式打开文件 r+,除了读,还可以写入。 w, 以只写的方式打开,如果文件不存在,则创建这个文件,并写放内容,如果文件存在,并原来有内容,则会清除原文件中所有内容,再写入(打开已有的重要文件) w+,除了可以写用fwrite, 还可以读fread a,以只写的方式打开,如果文件不存在,则创建这个文件,并写放内容,如果文件存在,并原来有内容,则不清除原有文件内容,再原有文件内容的最后写入新内容,(追加) a+,除了可以写用fwrite, 还可以读fread b,以二进制模式打开文件(图,电影) t,以文本模式打开文件

    注意:

    r+具有读写属性,从文件头开始写,保留原文件中没有被覆盖的内容; w+具有读写属性,写的时候如果文件存在,会被清空,从头开始写。

    返回的是一个文件资源

    5.fwrite(file,content)

    文件写入功能,file是文件资源,用fopen函数获取来的,content是写入内容。同 fputs 函数。 例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php 
    $file = fopen("1.txt","r+");
    $result = fwrite($file,"xx");
    if($result){
    echo "Success";
    }else
    echo "Failed";
    }
    ?>

    则从头开始写入资源,即把前两个字符设为 xx

    6. fread(file,size)

    读取文件指定部分的长度,file是文件资源,由fopen返回的对象,size是读取字符的长度。 例如

    1
    2
    3
    4
    5
    <?php 
    $file = fopen("1.txt","r");
    $content = fread($file,filesize("1.txt"));
    echo $content;
    ?>

    不过,上述的 filesize 方法只能获取本地文件大小,对于远程文件的读取就要换一种方法了。 例如

    1
    2
    3
    4
    5
    6
    7
    8
    <?php 
    $file = fopen("http://www.qq.com","r");
    $str = "";
    while(!feof($file)){  //判断时候到了文件结尾
    $str.=fread($file,1024);
    }
    echo $str;
    ?>

    7.fgets(file)

    file是文件资源,每次读取一行。例如我们读取出腾讯首页一共有多少行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     <?php 
    $file = fopen("http://www.qq.com","r");
    $str = "";
    $count = 0;
    while(!feof($file)){
    $str .= fgets($file);
    $count ++;
    }
    echo $count;
    ?>

    会输出结果 8893,我们可以查看源文件,看看它一共有多少行,验证一下即可。

    7.fgetc(file)

    与fgets方法很相似,file是文件资源,每次读取个字符。例如我们读取出腾讯首页一共有多少个字符。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php 
    $file = fopen("http://www.qq.com","r");
    $str = "";
    $count = 0;
    while(!feof($file)){
    $str .= fgetc($file);
    $count ++;
    }
    echo $count;
    ?>

    上述代码便会输出所有的字符数量。

    8.ftell(file)

    ftell 是返回当前读文件的指针位置,file 是文件资源,是由 fopen 返回的对象。

    9.fseek(file,offset,whence)

    file 文件系统指针,是典型地由 fopen() 创建的 resource(资源)。 offset 偏移量。 要移动到文件尾之前的位置,需要给 offset 传递一个负值,并设置 whence 为 SEEK_END。 whence

    SEEK_SET - 设定位置等于 offset 字节。 SEEK_CUR - 设定位置为当前位置加上 offset。 SEEK_END - 设定位置为文件尾加上 offset。

    10.rewind($file)

    回到文件头部,file是文件资源 例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?php 
    $file = fopen("1.txt","r");
    echo ftell($file)."<br>"; //输出读取前的指针位置
    echo fread($file,10)."<br>"; //读取10个字符,指针移动10个单位
    echo ftell($file)."<br>"; //输出读取完之后当前指针位置
    fseek($file,20,SEEK_CUR); //当前指针前移20单位
    echo ftell($file)."<br>"; //输出移动之后指针的位置
    echo fread($file,10)."<br>"; //输出读取的10个字符
    echo ftell($file)."<br>"; //输出读完10个字符之后指针的位置
    fseek($file,-20,SEEK_END); //指针移动到文件末尾前20个字符
    echo ftell($file)."<br>"; //输出移动之后指针的位置
    echo fread($file,10)."<br>"; //输出文件末尾20个字符
    echo ftell($file)."<br>"; //输出读完10个字符之后指针的位置
    rewind($file); //回到文件头部
    echo ftell($file)."<br>"; //输出移动之后指针的位置
    ?>

    运行结果:

    0 cuiqingcai 10 30 uiqingcai. 40 374 i.comcuiqi 384 0

    11.flock(file,operation[,wouldblock])

    file 文件资源指针,是典型地由 fopen() 创建的 resource(资源)。 operation operation 可以是以下值之一:

    LOCK_SH取得共享锁定(读取的程序)。 LOCK_EX 取得独占锁定(写入的程序。 LOCK_UN 释放锁定(无论共享或独占)。

    如果不希望 flock() 在锁定时堵塞,则是 LOCK_NB(Windows 上还不支持)。 wouldblock 如果锁定会堵塞的话(EWOULDBLOCK 错误码情况下),可选的第三个参数会被设置为 TRUE。(Windows 上不支持) 例如

    1
    2
    3
    4
    5
    6
    7
    <?php 
    $file = fopen("1.txt","a");
    if(flock($file,LOCK_EX)){
    fwrite($file,"xxx");
    flock($file,LOCK_UN);
    }
    ?>

    PHP

    在PHP中,我们进行字符串处理时,能用字符串处理函数时我们当然要使用简单的字符串处理函数,但字符串处理函数的能力是有限的,所以我们就需要利用一个更强大的工具,那就是正则表达式。

    简述正则表达式

    正则表达式是什么?

    正则表达式就是描述字符串排列模式的一种自定义语法规则。正则表达式就是通过构建具有特定规则的模式,和输入字符串信息进行比较,然后进行分割、匹配、查找、替换等等的相关操作。正则表达式不是PHP中独有的,多种语言均可以使用正则表达式,在这里我们介绍正则在PHP中的用法。

    使用场合

    1. PHP中,如果可以用字符串处理函数完成的任务,我们就不要使用正则表达式。 2. 有一些复杂的操作,例如格式检验等等,只能用正则表达式完成。 3. 正则表达式也是一个字符串,但是它是具有特殊意义的字符串。 4. 它具有一些编写规则,也是一种模式,也可以把它看做一种编程语言。 5. 只有把正则表达式运用到某个函数中使用,才能发挥出正则表达式的作用,否则,它便是一个简单的字符串。

    小例子

    图片的匹配例子

    1
    "/\<img\s*src=\".*?\"\/\>/iu"

    这就是匹配HTML中的一个图片标签,例如可以匹配

    1
    <img src="a.jpg"/>

    PHP中的正则表达式函数库

    在PHP中,有两套正则表达式函数库。

    1.POSIX 扩展的正则表达式函数(ereg)。 2.Perl 兼容的正则表达式函数(preg)。

    Perl 兼容的函数库是后加的函数库,功能更加强大,另外JavaScript 和 Perl 语言里面也会兼容这种模式,所以推荐学习 Perl 语言兼容的函数库。 在学习时,我们就需要学习下面两点

    1. 正则表达式的语法 2. PHP中正则表达式的处理函数

    正则表达式的语法

    任何正则表达式都是由定界符号、原子、元字符、模式修正符号四部分来组成的。下面我们以上面的例子依次来介绍这四部分内容。

    1
    "/\<img\s*src=\".*?\"\/\>/iu"

    1. 定界符号

    除了字母,数字和反斜杠 \ 以外的所有字符均可以为定界符。上面的例子中,开头和末尾的两个斜线 / 便作为定界符,我们还可以利用其它字符来定义,例如 | |,{ },# #等等,不过我们一般是使用斜线 / 作为定界符。

    2. 原子

    上面的例子中,img \s 均为原子。原子是正则表达式的最基本的组成单位,而且必须至少包含一个原子,正则表达式中可以单独使用的字符,就是原子。 使用时注意事项总结如下 1)原子包括所有打印字符和非打印字符,打印字符就是我们可以在屏幕上看到的字符例如abc等等,非打印字符就是我们看不到的字符,例如空格,回车等等。 2)\. \* \+ \? \< \( \< \>,所有有意义的字符,如果想作为原子使用,必须统统使用 \ 转义字符转义。 3)转义字符 \ 可以将有意义的字符转为有意义的字符,例如 是元字符,代表匹配一个字符零到多次,但是 \ 就代表了 * 这个字符。 4)另外转义字符还可以将没有意义的字符转成有意义的字符,例如 \d 可以表示任意一个十进制数字。总结如下:

    \d:表示任意一个十进制的数字。 \D:表示任意一个除数字以外的字符。 \s:表示任意一个空白字符,如空格、回车、\t、\n \S:表示任意一个非空白 \w:表示任意一个字,包括 a-z,A-Z,0-9,下划线_ \W:表示任意一个非字,\w 之外的任意一个字符

    5)自定义原子表,定义一个中括号,表示匹配中括号中的任何一个内容,注意是任何一个。例如

    正则表达式 “/[123]/“ 和字符串 “abc2”可以匹配成功。 正则表达式 “/[a-z]/“ 和字符串 “x3”可以匹配成功。 正则表达式 “/[1-3a-z]/“ 和字符串 “x”可以匹配成功。

    在这里中括号里面写入^代表取反,表示除了原子表中的原子,但是^必须写在[]的第一个字符上。例如

    a-z代表除了a-z的所有字符 \\n\\t代表除了换行和制表的所有字符

    3. 元字符

    上面的例子中, ?均为元字符。元字符就是用来修饰原子的字符,不可以单独出现。 例如 代表匹配前面的原子零到多次,但如果想匹配 ,则必须要加转义号 \ 来表示 ,特殊地,. 点也可以表示原子 元字符总结分类如下 1)* 表示其前面的原子可以出现 0 至多次 2)+ 表示前面的原子可以出现 1 至多次,即至少一个原子 3)? 表示前面的原子可以出现 0 次或 1 次 4){ } 用于自己定义前面原子出现的次数,另外分为以下几种

    {m},m表示一个整数,则前面的数字必须出现m次 {m,n},m,n表示两个整数,m<n,表示前面出现的原子最少出现 m 次,最多出现 n 次。 {m,} m表示一个整数,表示最少出现 m 次,最多无限 所以 * 可以表示为 {0,},+ 可以表示为{1,},? 表示为 {0,1}

    5). 默认情况下,表示除换行符外任意一个字符,和 * 组合表示可以匹配任意字符串,和 + 组合表示至少一个字符串 6)^ 直接在一个正则表达式的第一个字符出现,表示字符串必须以这个正则表达式开始,例如

    ^okay,表示字符串必须以 okay 开头

    7)$ 直接在一个正则表达式的最后一个字符出现,则表示字符串必须以这个正则表达式结尾,例如

    okay$,表示字符串必须以 okay 结尾。 注意:^okay$只是匹配一个okay字符,若要匹配 okay 开头okay结尾,则正则表达式可以写为 ^okay.*okay$

    8)| 表示或的关系,它的优先级是最低的,最后考虑它的功能 9)\b 表示一个边界,例如

    字符串 this is island,那么 \bis\b 则可以匹配,它匹配的是中间的 is,is两边是有边界的。

    10)\B 表示一个非边界,例如

    \Bis\b,则匹配第一个is,\bis\B则匹配第三个 is

    11)() 括号,重点,作用总结如下

    (1)将括号里面的元素作为一个大原子使用 即作为一个整体使用,例如 (abc)+,可以匹配abcabc…. (2)改变优先级 加上括号可以提高其优先级别。 (3)作为子模式使用 正则表达式中添加括号相当于添加了子模式。全部匹配作为一个大模式,放到数组的第一个元素中,每个()是一个子模式按顺序放到数组的其它元素中去。 在下面所说的 preg_match 方法中,结果会被赋值到 $arr 变量中,先匹配外层模式,再匹配内层模式。

    1
    2
    3
    4
    5
    6
    $pattern="/(\d{4}(\W)\d{2}\W\d{2})\s+(\d{2}(\W)\d{2}\W\d{2})\s+(?:am|pm)/"; //正则表达式模式
    $string="today is 2010/09/15 15:35:28 pm..."; //需要和上面模式字符串进行匹配的变量字符串
    if(preg_match($pattern, $string, $arr)){
    print_r($arr);
    }

    结果

    1
    2
    3
    4
    5
    6
    7
    8
    Array
    (
    [0] => 2010/09/15 15:35:28 pm
    [1] => 2010/09/15
    [2] => /
    [3] => 15:35:28
    [4] => :
    )

    (4)取消子模式 就将它作为大原子或者改变优先级使用。 只要在模式前面加一个 ?: 就可以取消子模式,例如

    1
    (?:am|pm) //即不把(am|pm)作为一个子模式,直接将 am|pm 看做一个整体来使用。

    (5)反向引用 可以在模式中直接将子模式取出来,再作为正则表达式模式的一部分, 如果是在正则表达式像替换函数preg_replace函数中, 可以将子模式取出, 在被替换的字符串中使用 \1 取第一个子模式、 \2取第二个子模式, …. \5 (注意是单引号还是双引号引起来的正则) “\\1” 在双引号引起来的正则,它是可以解释转义字符的,所以 \1 必须要写成 \\1 ‘\1’ 在单引号引起来的正则中,则不会出现这种情况,\1 仍然可以写成 \1 例如

    1
    2
    3
    4
    5
    6
    7
    $pattern="/\d{4}(\W)\d{2}\\1\d{2}\s+\d{2}(\W)\d{2}\\2\d{2}\s+(?:am|pm)/"; //正则表达式模式
    $string="today is 2010/09/15 15:35:28 pm..."; //需要和上面模式字符串进行匹配的变量字符串
    if(preg_match($pattern, $string, $arr)){
    echo "正则表达式 <b>{$pattern} </b>和字符串 <b>{$string}</b> 匹配成功<br>";
    print_r($arr);
    echo '</pre>';
    }

    在上面的例子中,因为(\W)已经匹配了第一个斜杠/,它已经作为第一个子模式保存起来了,我们想继续使用这个匹配,我们就可以用 \\1 来表示,如果是单引号引起来的正则,则直接用 \1 来表示就可以了,后面的 \\2 和它是同样的原理。

    4. 模式修正符号

    上面的例子中,最后 / 后面的 i u 即为模式修正符号。

    1)就是几个字母 2)可以一次使用一个,每一个都具有一定的意义,也可以连续使用多个符号 3)放在最后面,是对整个正则表达式调优用的,也可以说是对正则表达式功能的扩展。 归类如下: i:表示在和模式进行匹配时不区分大小写 m:将每一行字符视为新的一行,也就是换行符后任何一行都可以当做一个整体字符串,主要的影响就在于 ^ 号和 $ 号。 例如:

    1
    2
    3
    4
    5
    $pattern = '/^abc/m'
    $string ='cde
    abcd
    efg'
    //可以匹配成功,因为加入m之后,第二行的abcd也当做一行了,也算是以abc开头

    s:如果没有使用这个模式修正符号,那么 “.” 是不能当做换行符的,加入s,将字符串视为单行,那么 “.” 也就可以表示换行符了。 x:模式中的空白忽略不计,注意是模式中的。

    1
    2
    3
    $pattern = '/a b c/x'
    $string ='abc'
    //可以匹配成功,因为加入x之后,a b c直接当做abc来看

    e:正则表达式必须使用在 preg_replace 函数中使用 A:必须以正则表达式开头,也就相当于前面加了 ^ Z:必须以正则表达式结尾,相当于后面加了 $ U:取消贪婪模式,或者 .*? 也可以取消贪婪模式,不过两个同时使用时会导致相反的结果,相当于负负得正的影响,同时出现则会又开启了贪婪模式 。

    综上所述,正则表达式的组成为 /原子和元字符/模式修正符号,其中 / 为定界符号,不过有一些语言是不需要这个定界符的,例如 Python 等。

    5.元字符的优先级(了解)

    1)\ 转义符最高 2)(),(?:),{ },括号其次 3)* + ? {} 4)^ $ \b 5)|

    6.常用的正则表达式

    1)校验数字的表达式

    1 数字:^[0-9]$ 2 n位的数字:^\d{n}$ 3 至少n位的数字:^\d{n,}$ 4 m-n位的数字:^\d{m,n}$ 5 零和非零开头的数字:^(0|[1-9][0-9])$ 6 非零开头的最多带两位小数的数字:^([1-9][0-9])+(.[0-9]{1,2})?$ 7 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$ 8 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$ 9 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$ 10 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$ 11 非零的正整数:^[1-9]\d$ 或 ^([1-9][0-9]){1,3}$ 或 ^\+?[1-9][0-9]$ 12 非零的负整数:^\-[1-9][]0-9”$ 或 ^-[1-9]\d$ 13 非负整数:^\d+$ 或 ^[1-9]\d|0$ 14 非正整数:^-[1-9]\d|0$ 或 ^((-\d+)|(0+))$ 15 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d\.\d|0\.\d[1-9]\d|0?\.0+|0$ 16 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d\.\d|0\.\d[1-9]\d))|0?\.0+|0$ 17 正浮点数:^[1-9]\d\.\d|0\.\d[1-9]\d$ 或 ^(([0-9]+\.[0-9][1-9][0-9])|([0-9][1-9][0-9]\.[0-9]+)|([0-9][1-9][0-9]))$ 18 负浮点数:^-([1-9]\d\.\d|0\.\d[1-9]\d)$ 或 ^(-(([0-9]+\.[0-9][1-9][0-9])|([0-9][1-9][0-9]\.[0-9]+)|([0-9][1-9][0-9])))$ 19 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d\.\d|0\.\d[1-9]\d|0?\.0+|0)$

    2)校验字符的表达式

    1 汉字:^[\u4e00-\u9fa5]{0,}$ 2 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$ 3 长度为3-20的所有字符:^.{3,20}$ 4 由26个英文字母组成的字符串:^[A-Za-z]+$ 5 由26个大写英文字母组成的字符串:^[A-Z]+$ 6 由26个小写英文字母组成的字符串:^[a-z]+$ 7 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$ 8 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$ 9 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$ 10 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$ 11 可以输入含有^%&’,;=?$\“等字符:%&',;=?$\\x22+ 12 禁止输入含有~的字符:~\\x22+

    3)特殊需求表达式

    1 Email地址:^\w+([-+.]\w+)@\w+([-.]\w+)\.\w+([-.]\w+)$ 2 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.? 3 InternetURL:[a-zA-z]+://\\s 或 ^http://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?$ 4 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$ 5 电话号码(“XXX-XXXXXXX”、”XXXX-XXXXXXXX”、”XXX-XXXXXXX”、”XXX-XXXXXXXX”、”XXXXXXX”和”XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$ 6 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7} 7 身份证号(15位、18位数字):^\d{15}|\d{18}$ 8 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$ 9 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 10 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$ 11 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.\d)(?=.[a-z])(?=.[A-Z]).{8,10}$ 12 日期格式:^\d{4}-\d{1,2}-\d{1,2} 13 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$ 14 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$ 15 钱的输入格式: 16 1.有四种钱的表示形式我们可以接受:”10000.00” 和 “10,000.00”, 和没有 “分” 的 “10000” 和 “10,000”:^[1-9][0-9]$ 17 2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符”0”不通过,所以我们采用下面的形式:^(0|[1-9][0-9])$ 18 3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9])$ 19 4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$ 20 5.必须说明的是,小数点后面至少应该有1位数,所以”10.”是不通过的,但是 “10” 和 “10.2” 是通过的:^[0-9]+(.[0-9]{2})?$ 21 6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$ 22 7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})(.[0-9]{1,2})?$ 23 8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3}))(.[0-9]{1,2})?$ 24 备注:这就是最终结果了,别忘了”+”可以用”“替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里 25 xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$ 26 中文字符的正则表达式:[\u4e00-\u9fa5] 27 双字节字符:\\x00-\\xff (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)) 28 空白行的正则表达式:\n\s\r (可以用来删除空白行) 29 HTML标记的正则表达式:<(\S?)>>.?</\1>|<.? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力) 30 首尾空白字符的正则表达式:^\s|\s$或(^\s)|(\s$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式) 31 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始) 32 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字) 33 IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)

    PHP正则表达式处理函数

    1. int preg_match ( string pattern, string subject [, array matches [, int flags]] )

    用法:

    1)在 subject 字符串中搜索与 pattern 给出的正则表达式相匹配的内容。 2)如果提供了 matches,则其会被搜索的结果所填充。$matches[0] 将包含与整个模式匹配的文本,$matches[1] 将包含与第一个捕获的括号中的子模式所匹配的文本,以此类推。 3)flags 参数自 PHP 4.3.0 起可用,flags 可以是下列标记: PREG_OFFSET_CAPTURE 如果设定本标记,对每个出现的匹配结果也同时返回其附属的字符串偏移量。注意这改变了返回的数组的值,使其中的每个单元也是一个数组,其中第一项为匹配字符串,第二项为其偏移量。

    例如下面的例子,匹配一个URL

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $str="这是一个正则表https://www.baidu.com达式的匹配函数";

    $url="/(https?|ftps?):\/\/((www|mail|news)\.([^\.\/]+)\.(com|org|net))/i";
    if(preg_match($url, $str, $arr)){
    echo "字符串中有正确的URL信息<br>";
    echo '<pre>';
    print_r($arr);
    echo '</pre>';

    echo "主机:".$arr[2]."<br>";

    }else{
    echo "字符串中不包括URL";
    }

    2. int preg_match_all ( string pattern, string subject [, array matches [, int flags]] )

    与 preg_match_all 用法完全一致,不过它匹配的是所有的信息。 例如下面的例子,匹配的是所有符合正则表达式的内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    $str="这是一个正则表https://www.baidu.com达式的匹配函数
    这是一个正则表http://www.baidu1.com达式的匹配函数
    这是一个正则表https://mail.baidu2.com达式的匹配函数
    这是一个正则表https://news.baidu3.com达式的匹配函数
    这是一个正则表https://www.baidu4.org达式的匹配函数
    这是一个正则表https://www.baidu5.net达式的匹配函数
    这是一个正则表ftps://www.baidu6.com达式的匹配函数
    这是一个正则表ftp://www.google7.com达式的匹配函数
    这是一个正则表https://www.baidu7.net达式的匹配函数
    ";
    $url="/(https?|ftps?):\/\/((www|mail|news)\.([^\.\/]+)\.(com|org|net))/i";

    if(preg_match_all($url, $str, $arr)){
    echo "字符串中有正确的URL信息<br>";
    echo '<pre>';
    print_r($arr);
    echo '</pre>';

    echo "主机:".$arr[2]."<br>";

    }else{
    echo "字符串中不包括URL";
    }

    上面的输出结果 $arr 的结果没有将一次次匹配的结果分开,也就是所有的子模式都写入一个数组中,没有将结果分成单独的一个数组。 结果中 第零个数组包含了所有的全模式,第一个数组包含了所有的第一个子模式。 可以通过修改上面的代码,实现结果的分开显示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if(preg_match_all($url, $str, $arr,PREG_SET_ORDER)){
    echo "字符串中有正确的URL信息<br>";
    echo '<pre>';
    print_r($arr);
    echo '</pre>';

    echo "主机:".$arr[2]."<br>";

    }else{
    echo "字符串中不包括URL";
    }

    在上面的 preg_match_all 方法中,第四个参数传入 PREG_SET_ORDER,即可实现每一个全模式和子模式的分开展示。 其实,默认模式的参数即为 PREG_PATTERN_ORDER,传入该参数即相当于不传入参数。

    3.mixed preg_replace(mixed pattern,mixed replacement,mixed subject [,int limit])

    替换函数,pattern是正则表达式,replacement是替换成的内容,也就是把匹配到符合正则表达式的内容替换为 replacement,subject是查找的字符串,即替换的是这个字符串里面的内容。

    1
    2
    3
    4
    5
    $str=" 字符串中php的替换函数,系统提供的 字符串中的替换函数,系统提供的 字符串中的替换函数,系统提供的 字符串中的替换函数,系统提供的 字lamp符串中的替换函数,系统php提供的 字符串中的替换函数";

    echo $str."<br>";

    echo preg_replace("/[a-zA-z]+/",'',$str);

    执行该程序就会将str字符串中的字母替换为空。 另外,可以指定替换的次数,也就是第四个参数

    1
    2
    3
    4
    5
    $str=" 字符串中php的替换函数,系统提供的 字符串中的替换函数,系统提供的 字符串中的替换函数,系统提供的 字符串中的替换函数,系统提供的 字lamp符串中的替换函数,系统php提供的 字符串中的替换函数";

    echo $str."<br>";

    echo preg_replace("/[a-zA-z]+/",'',$str,3);

    这样,就可以实现只替换三次。 另外,可以实现多对多数组的替换,这是一个比较常用的方法,例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    $str=" 字符串中php的替换函数,系统提供的 字符串中的替换函数,系统提供的 字符串中的替换函数,系统提供的 字符串中的替换函数,系统提供的 字lamp符串中的替换函数,[b]系统php提供的 字符串中[/b]的替换函数,系统提供php的 字符串中的替换函数,系统提供的 字符串中的替换函数,系统提供的 字符<b>apache</b>串中的替换函数,系统提供的 [u]mysql字符串中的替换函[/u]数,系统Apache提供的 字符串中的MySQL替换函数,系php统提供的 [i]字符串中的替换函数[/i],系统提供的[size=7] 字符串中的php替换[/size]函数,系统提供的[color=Magenta] 字linux符串中的替[/color]换mysql函数,系统提供的php 字符串中的替换函数,系统提供的 字符串中的替换函数,系统php提供的 字符串中的替换函数,系统提供的 字[align=center]符串[b]系统php提供的 字符串中[/b]中的替换函数[/align],系[b]系统php提供的 字符串中[/b]统提php供的 字符串中的替换函数,系统提供的";

    echo $str."<br>";

    $ubbcodes=array(
    '/[b](.*?)[\/b]/i',
    '/[u](.*?)[\/u]/i',
    '/[i](.*?)[\/i]/i',
    '/[color=(.*?)](.*?)[\/color]/',
    '/[size=(.*?)](.*?)[\/size]/',
    '/[align=(.*?)](.*?)[\/align]/'
    );

    $htmls=array(
    '<b>\1</b>',
    '<u>\1</u>',
    '<i>\1</i>',
    '<font color="\1">\2</font>',
    '<font size="\1">\2</font>',
    '<p align="\1">\2</p>'
    );

    echo preg_replace($ubbcodes,$htmls,$str);

    上面就可以实现中括号标签转化为HTML标签。 好,关于正则表达式函数,掌握这三个就基本够用了,其他的还有正则表达式分割函数,直接用PHP里面的字符串分割即可,不在此叙述了。

    本篇总结

    本篇介绍了正则表达式的相关语法以及在PHP中的正则表达式匹配函数,以上所有便是PHP正则表达式的相关内容,希望对大家有帮助~

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    如下为原文。

    经过多次尝试,模拟登录淘宝终于成功了,实在是不容易,淘宝的登录加密和验证太复杂了,煞费苦心,在此写出来和大家一起分享,希望大家支持。

    温馨提示

    更新时间,2016-02-01,现在淘宝换成了滑块验证了,比较难解决这个问题,以下的代码没法用了,仅作学习参考研究之用吧。

    本篇内容

    1. python 模拟登录淘宝网页 2. 获取登录用户的所有订单详情 3. 学会应对出现验证码的情况 4. 体会一下复杂的模拟登录机制

    探索部分成果

    1. 淘宝的密码用了 AES 加密算法,最终将密码转化为 256 位,在 POST 时,传输的是 256 位长度的密码。 2. 淘宝在登录时必须要输入验证码,在经过几次尝试失败后最终获取了验证码图片让用户手动输入来验证。 3. 淘宝另外有复杂且每天在变的 ua 加密算法,在程序中我们需要提前获取某一 ua 码才可进行模拟登录。 4. 在获取最后的登录 st 码时,历经了多次请求和正则表达式提取,且 st 码只可使用一次。

    整体思路梳理

    1. 手动到浏览器获取 ua 码以及 加密后的密码,只获取一次即可,一劳永逸。 2. 向登录界面发送登录请求,POST 一系列参数,包括 ua 码以及密码等等,获得响应,提取验证码图像。 3. 用户输入手动验证码,重新加入验证码数据再次用 POST 方式发出请求,获得响应,提取 J_Htoken。 4. 利用 J_Htoken 向 alipay 发出请求,获得响应,提取 st 码。 5. 利用 st 码和用户名,重新发出登录请求,获得响应,提取重定向网址,存储 cookie。 6. 利用 cookie 向其他个人页面如订单页面发出请求,获得响应,提取订单详情。 是不是没看懂?没事,下面我将一点点说明自己模拟登录的过程,希望大家可以理解。

    前期准备

    由于淘宝的 ua 算法和 aes 密码加密算法太复杂了,ua 算法在淘宝每天都是在变化的,不过,这个内容你获取之后一直用即可,经过测试之后没有问题,一劳永逸。 那么 ua 和 aes 密码怎样获取呢? 我们就从浏览器里面直接获取吧,打开浏览器,找到淘宝的登录界面,按 F12 或者浏览器右键审查元素。 在这里我用的是火狐浏览器,首先记得在浏览器中设置一下显示持续日志,要不然页面跳转了你就看不到之前抓取的信息了。在这里截图如下: 20150225013600 好,那么接下来我们就从浏览器中获取 ua 和 aes 密码 点击网络选项卡,这时都是空的,什么数据也没有截取。这时你就在网页上登录一下试试吧,输入用户名啊,密码啊,有必要时需要输入验证码,点击登录。 QQ截图20150225014124 等跳转成功后,你就可以看到好多日志记录了,点击图中的那一行 login.taobo.com,然后查看参数,你就会发现表单数据了,其中就包括 ua 还有下面的 password2,把这俩复制下来,我们之后要用到的。这就是我们需要的 ua 还有 aes 加密后的密码。 QQ截图20150225014019 恩,读到这里,你应该获取到了属于自己的 ua 和 password2 两个内容。

    输入验证码并获取 J_HToken

    经过博主本人亲自验证,有时候,在模拟登录时你并不需要输入验证码,它直接返回的结果就是前面所说的下一步用到的 J_Token,而有时候你则会需要输入验证码,等你手动输入验证码之后,重新请求登录一次。 博主是边写程序边更新文章的,现在写完了是否有必要输入验证码的检验以及在浏览器中呈现验证码。 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-

    import urllib
    import urllib2
    import cookielib
    import re
    import webbrowser

    #模拟登录淘宝类
    class Taobao:

    #初始化方法
    def __init__(self):
    #登录的URL
    self.loginURL = "https://login.taobao.com/member/login.jhtml"
    #代理IP地址,防止自己的IP被封禁
    self.proxyURL = 'http://120.193.146.97:843'
    #登录POST数据时发送的头部信息
    self.loginHeaders = {
    'Host':'login.taobao.com',
    'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0',
    'Referer' : 'https://login.taobao.com/member/login.jhtml',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Connection' : 'Keep-Alive'
    }
    #用户名
    self.username = 'cqcre'
    #ua字符串,经过淘宝ua算法计算得出,包含了时间戳,浏览器,屏幕分辨率,随机数,鼠标移动,鼠标点击,其实还有键盘输入记录,鼠标移动的记录、点击的记录等等的信息
    self.ua = '191UW5TcyMNYQwiAiwTR3tCf0J/QnhEcUpkMmQ=|Um5Ockt0TXdPc011TXVKdyE=|U2xMHDJ+H2QJZwBxX39Rb1d5WXcrSixAJ1kjDVsN|VGhXd1llXGNaYFhkWmJaYl1gV2pIdUtyTXRKfkN4Qn1FeEF6R31TBQ==|VWldfS0TMw8xDjYWKhAwHiUdOA9wCDEVaxgkATdcNU8iDFoM|VmNDbUMV|V2NDbUMV|WGRYeCgGZhtmH2VScVI2UT5fORtmD2gCawwuRSJHZAFsCWMOdVYyVTpbPR99HWAFYVMpUDUFORshHiQdJR0jAT0JPQc/BDoFPgooFDZtVBR5Fn9VOwt2EWhCOVQ4WSJPJFkHXhgoSDVIMRgnHyFqQ3xEezceIRkmahRqFDZLIkUvRiEDaA9qQ3xEezcZORc5bzk=|WWdHFy0TMw8vEy0UIQE0ADgYJBohGjoAOw4uEiwXLAw2DThu9a==|WmBAED5+KnIbdRh1GXgFQSZbGFdrUm1UblZqVGxQa1ZiTGxQcEp1I3U=|W2NDEz19KXENZwJjHkY7Ui9OJQsre09zSWlXY1oMLBExHzERLxsuE0UT|XGZGFjh4LHQdcx5zH34DRyBdHlFtVGtSaFBsUmpWbVBkSmpXd05zTnMlcw==|XWdHFzl5LXUJYwZnGkI/VitKIQ8vEzMKNws3YTc=|XmdaZ0d6WmVFeUB8XGJaYEB4TGxWbk5yTndXa0tyT29Ta0t1QGBeZDI='
    #密码,在这里不能输入真实密码,淘宝对此密码进行了加密处理,256位,此处为加密后的密码
    self.password2 = '7511aa68sx629e45de220d29174f1066537a73420ef6dbb5b46f202396703a2d56b0312df8769d886e6ca63d587fdbb99ee73927e8c07d9c88cd02182e1a21edc13fb8e140a4a2a4b5c253bf38484bd0e08199e03eb9bf7b365a5c673c03407d812b91394f0d3c7564042e3f2b11d156aeea37ad6460118914125ab8f8ac466f'
    self.post = post = {
    'ua':self.ua,
    'TPL_checkcode':'',
    'CtrlVersion': '1,0,0,7',
    'TPL_password':'',
    'TPL_redirect_url':'http://i.taobao.com/my_taobao.htm?nekot=udm8087E1424147022443',
    'TPL_username':self.username,
    'loginsite':'0',
    'newlogin':'0',
    'from':'tb',
    'fc':'default',
    'style':'default',
    'css_style':'',
    'tid':'XOR_1_000000000000000000000000000000_625C4720470A0A050976770A',
    'support':'000001',
    'loginType':'4',
    'minititle':'',
    'minipara':'',
    'umto':'NaN',
    'pstrong':'3',
    'llnick':'',
    'sign':'',
    'need_sign':'',
    'isIgnore':'',
    'full_redirect':'',
    'popid':'',
    'callback':'',
    'guf':'',
    'not_duplite_str':'',
    'need_user_id':'',
    'poy':'',
    'gvfdcname':'10',
    'gvfdcre':'',
    'from_encoding ':'',
    'sub':'',
    'TPL_password_2':self.password2,
    'loginASR':'1',
    'loginASRSuc':'1',
    'allp':'',
    'oslanguage':'zh-CN',
    'sr':'1366*768',
    'osVer':'windows|6.1',
    'naviVer':'firefox|35'
    }
    #将POST的数据进行编码转换
    self.postData = urllib.urlencode(self.post)
    #设置代理
    self.proxy = urllib2.ProxyHandler({'http':self.proxyURL})
    #设置cookie
    self.cookie = cookielib.LWPCookieJar()
    #设置cookie处理器
    self.cookieHandler = urllib2.HTTPCookieProcessor(self.cookie)
    #设置登录时用到的opener,它的open方法相当于urllib2.urlopen
    self.opener = urllib2.build_opener(self.cookieHandler,self.proxy,urllib2.HTTPHandler)


    #得到是否需要输入验证码,这次请求的相应有时会不同,有时需要验证有时不需要
    def needIdenCode(self):
    #第一次登录获取验证码尝试,构建request
    request = urllib2.Request(self.loginURL,self.postData,self.loginHeaders)
    #得到第一次登录尝试的相应
    response = self.opener.open(request)
    #获取其中的内容
    content = response.read().decode('gbk')
    #获取状态吗
    status = response.getcode()
    #状态码为200,获取成功
    if status == 200:
    print u"获取请求成功"
    #\u8bf7\u8f93\u5165\u9a8c\u8bc1\u7801这六个字是请输入验证码的utf-8编码
    pattern = re.compile(u'\u8bf7\u8f93\u5165\u9a8c\u8bc1\u7801',re.S)
    result = re.search(pattern,content)
    #如果找到该字符,代表需要输入验证码
    if result:
    print u"此次安全验证异常,您需要输入验证码"
    return content
    #否则不需要
    else:
    print u"此次安全验证通过,您这次不需要输入验证码"
    return False
    else:
    print u"获取请求失败"

    #得到验证码图片
    def getIdenCode(self,page):
    #得到验证码的图片
    pattern = re.compile('<img id="J_StandardCode_m.*?data-src="(.*?)"',re.S)
    #匹配的结果
    matchResult = re.search(pattern,page)
    #已经匹配得到内容,并且验证码图片链接不为空
    if matchResult and matchResult.group(1):
    print matchResult.group(1)
    return matchResult.group(1)
    else:
    print u"没有找到验证码内容"
    return False

    #程序运行主干
    def main(self):
    #是否需要验证码,是则得到页面内容,不是则返回False
    needResult = self.needIdenCode()
    if not needResult == False:
    print u"您需要手动输入验证码"
    idenCode = self.getIdenCode(needResult)
    #得到了验证码的链接
    if not idenCode == False:
    print u"验证码获取成功"
    print u"请在浏览器中输入您看到的验证码"
    webbrowser.open_new_tab(idenCode)
    #验证码链接为空,无效验证码
    else:
    print u"验证码获取失败,请重试"
    else:
    print u"不需要输入验证码"



    taobao = Taobao()
    taobao.main()

    恩,请把里面的 ua 和 password2 还有用户名换成自己的进行尝试,用我的可能会产生错误的。 运行结果 QQ截图20150225015508 然后会蹦出浏览器,显示了验证码的内容,这个需要你来手动输入。 在这里有小伙伴向我反映有这么个错误 QQ图片20150227181617 经过查证,竟然是版本问题,博主本人用的是 2.7.7,而小伙伴用的是 2.7.9。后来换成 2.7.7 就好了…,我也是醉了,希望有相同错误的小伙伴,可以尝试换一下版本… 好啦,运行时会弹出浏览器,如图 QQ截图20150225015717 那么,我们现在需要手动输入验证码,重新向登录界面发出登录请求,之前的 post 数据内容加入验证码这一项,重新请求一次,如果请求成功,则会返回下一步我们需要的 J_HToken,如果验证码输入错误,则会返回验证码输入错误的选项。好,下面,我已经写到了获取 J_HToken 的进度,代码如下,现在运行程序,会蹦出浏览器,然后提示你输入验证码,用户手动输入之后,则会返回一个页面,我们提取出 J_Htoken 即可。 注意,到现在为止,你还没有登录成功,只是获取到了 J_HToken 的值。 目前写到的代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-

    import urllib
    import urllib2
    import cookielib
    import re
    import webbrowser

    #模拟登录淘宝类
    class Taobao:

    #初始化方法
    def __init__(self):
    #登录的URL
    self.loginURL = "https://login.taobao.com/member/login.jhtml"
    #代理IP地址,防止自己的IP被封禁
    self.proxyURL = 'http://120.193.146.97:843'
    #登录POST数据时发送的头部信息
    self.loginHeaders = {
    'Host':'login.taobao.com',
    'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0',
    'Referer' : 'https://login.taobao.com/member/login.jhtml',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Connection' : 'Keep-Alive'
    }
    #用户名
    self.username = 'cqcre'
    #ua字符串,经过淘宝ua算法计算得出,包含了时间戳,浏览器,屏幕分辨率,随机数,鼠标移动,鼠标点击,其实还有键盘输入记录,鼠标移动的记录、点击的记录等等的信息
    self.ua = '191UW5TcyMNYQwiAiwTR3tCf0J/QnhEcUpkMmQ=|Um5Ockt0TXdPc011TXVKdyE=|U2xMHDJ+H2QJZwBxX39Rb1d5WXcrSixAJ1kjDVsN|VGhXd1llXGNaYFhkWmJaYl1gV2pIdUtyTXRKfkN4Qn1FeEF6R31TBQ==|VWldfS0TMw8xDjYWKhAwHiUdOA9wCDEVaxgkATdcNU8iDFoM|VmNDbUMV|V2NDbUMV|WGRYeCgGZhtmH2VScVI2UT5fORtmD2gCawwuRSJHZAFsCWMOdVYyVTpbPR99HWAFYVMpUDUFORshHiQdJR0jAT0JPQc/BDoFPgooFDZtVBR5Fn9VOwt2EWhCOVQ4WSJPJFkHXhgoSDVIMRgnHyFqQ3xEezceIRkmahRqFDZLIkUvRiEDaA9qQ3xEezcZORc5bzk=|WWdHFy0TMw8vEy0UIQE0ADgYJBohGjoAOw4uEiwXLAw2DThuOA==|WmBAED5+KnIbdRh1GXgFQSZbGFdrUm1UblZqVGxQa1ZiTGxQcEp1I3U=|W2NDEz19KXENZwJjHkY7Ui9OJQsre09zSWlXY1oMLBExHzERLxsuE0UT|XGZGFjh4LHQdcx5zH34DRyBdHlFtVGtSaFBsUmpWbVBkSmpXd05zTnMlcw==|XWdHFzl5LXUJYwZnGkI/VitKIQ8vEzMKNws3YTc=|XmdaZ0d6WmVFeUB8XGJaYEB4TGxWbk5yTndXa0tyT29Ta0t1QGBeZDI='
    #密码,在这里不能输入真实密码,淘宝对此密码进行了加密处理,256位,此处为加密后的密码
    self.password2 = '7511aa6854629e45de220d29174f1066537a73420ef6dbb5b46f202396703a2d56b0312df8769d886e6ca63d587fdbb99ee73927e8c07d9c88cd02182e1a21edc13fb8e0a4a2a4b5c253bf38484bd0e08199e03eb9bf7b365a5c673c03407d812b91394f0d3c7564042e3f2b11d156aeea37ad6460118914125ab8f8ac466f'
    self.post = post = {
    'ua':self.ua,
    'TPL_checkcode':'',
    'CtrlVersion': '1,0,0,7',
    'TPL_password':'',
    'TPL_redirect_url':'http://i.taobao.com/my_taobao.htm?nekot=udm8087E1424147022443',
    'TPL_username':self.username,
    'loginsite':'0',
    'newlogin':'0',
    'from':'tb',
    'fc':'default',
    'style':'default',
    'css_style':'',
    'tid':'XOR_1_000000000000000000000000000000_625C4720470A0A050976770A',
    'support':'000001',
    'loginType':'4',
    'minititle':'',
    'minipara':'',
    'umto':'NaN',
    'pstrong':'3',
    'llnick':'',
    'sign':'',
    'need_sign':'',
    'isIgnore':'',
    'full_redirect':'',
    'popid':'',
    'callback':'',
    'guf':'',
    'not_duplite_str':'',
    'need_user_id':'',
    'poy':'',
    'gvfdcname':'10',
    'gvfdcre':'',
    'from_encoding ':'',
    'sub':'',
    'TPL_password_2':self.password2,
    'loginASR':'1',
    'loginASRSuc':'1',
    'allp':'',
    'oslanguage':'zh-CN',
    'sr':'1366*768',
    'osVer':'windows|6.1',
    'naviVer':'firefox|35'
    }
    #将POST的数据进行编码转换
    self.postData = urllib.urlencode(self.post)
    #设置代理
    self.proxy = urllib2.ProxyHandler({'http':self.proxyURL})
    #设置cookie
    self.cookie = cookielib.LWPCookieJar()
    #设置cookie处理器
    self.cookieHandler = urllib2.HTTPCookieProcessor(self.cookie)
    #设置登录时用到的opener,它的open方法相当于urllib2.urlopen
    self.opener = urllib2.build_opener(self.cookieHandler,self.proxy,urllib2.HTTPHandler)


    #得到是否需要输入验证码,这次请求的相应有时会不同,有时需要验证有时不需要
    def needCheckCode(self):
    #第一次登录获取验证码尝试,构建request
    request = urllib2.Request(self.loginURL,self.postData,self.loginHeaders)
    #得到第一次登录尝试的相应
    response = self.opener.open(request)
    #获取其中的内容
    content = response.read().decode('gbk')
    #获取状态吗
    status = response.getcode()
    #状态码为200,获取成功
    if status == 200:
    print u"获取请求成功"
    #\u8bf7\u8f93\u5165\u9a8c\u8bc1\u7801这六个字是请输入验证码的utf-8编码
    pattern = re.compile(u'\u8bf7\u8f93\u5165\u9a8c\u8bc1\u7801',re.S)
    result = re.search(pattern,content)
    print content
    #如果找到该字符,代表需要输入验证码
    if result:
    print u"此次安全验证异常,您需要输入验证码"
    return content
    #否则不需要
    else:
    #返回结果直接带有J_HToken字样,表明直接验证通过
    tokenPattern = re.compile('id="J_HToken"')
    tokenMatch = re.search(tokenPattern,content)
    if tokenMatch:
    print u"此次安全验证通过,您这次不需要输入验证码"
    return False
    else:
    print u"获取请求失败"
    return None

    #得到验证码图片
    def getCheckCode(self,page):
    #得到验证码的图片
    pattern = re.compile('<img id="J_StandardCode_m.*?data-src="(.*?)"',re.S)
    #匹配的结果
    matchResult = re.search(pattern,page)
    #已经匹配得到内容,并且验证码图片链接不为空
    if matchResult and matchResult.group(1):
    print matchResult.group(1)
    return matchResult.group(1)
    else:
    print u"没有找到验证码内容"
    return False


    #输入验证码,重新请求,如果验证成功,则返回J_HToken
    def loginWithCheckCode(self):
    #提示用户输入验证码
    checkcode = raw_input('请输入验证码:')
    #将验证码重新添加到post的数据中
    self.post['TPL_checkcode'] = checkcode
    #对post数据重新进行编码
    self.postData = urllib.urlencode(self.post)
    try:
    #再次构建请求,加入验证码之后的第二次登录尝试
    request = urllib2.Request(self.loginURL,self.postData,self.loginHeaders)
    #得到第一次登录尝试的相应
    response = self.opener.open(request)
    #获取其中的内容
    content = response.read().decode('gbk')
    #检测验证码错误的正则表达式,\u9a8c\u8bc1\u7801\u9519\u8bef 是验证码错误五个字的编码
    pattern = re.compile(u'\u9a8c\u8bc1\u7801\u9519\u8bef',re.S)
    result = re.search(pattern,content)
    #如果返回页面包括了,验证码错误五个字
    if result:
    print u"验证码输入错误"
    return False
    else:
    #返回结果直接带有J_HToken字样,说明验证码输入成功,成功跳转到了获取HToken的界面
    tokenPattern = re.compile('id="J_HToken" value="(.*?)"')
    tokenMatch = re.search(tokenPattern,content)
    #如果匹配成功,找到了J_HToken
    if tokenMatch:
    print u"验证码输入正确"
    print tokenMatch.group(1)
    return tokenMatch.group(1)
    else:
    #匹配失败,J_Token获取失败
    print u"J_Token获取失败"
    return False
    except urllib2.HTTPError, e:
    print u"连接服务器出错,错误原因",e.reason
    return False

    #程序运行主干
    def main(self):
    #是否需要验证码,是则得到页面内容,不是则返回False
    needResult = self.needCheckCode()
    #请求获取失败,得到的结果是None
    if not needResult ==None:
    if not needResult == False:
    print u"您需要手动输入验证码"
    idenCode = self.getCheckCode(needResult)
    #得到了验证码的链接
    if not idenCode == False:
    print u"验证码获取成功"
    print u"请在浏览器中输入您看到的验证码"
    webbrowser.open_new_tab(idenCode)
    J_HToken = self.loginWithCheckCode()
    print "J_HToken",J_HToken
    #验证码链接为空,无效验证码
    else:
    print u"验证码获取失败,请重试"
    else:
    print u"不需要输入验证码"
    else:
    print u"请求登录页面失败,无法确认是否需要验证码"



    taobao = Taobao()
    taobao.main()

    现在的运行结果是这样的,我们已经可以得到 J_HToken 了,离成功又迈进了一步。 QQ截图20150225200329 好,到现在为止,我们应该可以获取到 J_HToken 的值啦。

    利用 J_HToken 获取 st

    st 也是一个经计算得到的 code,可以这么理解,st 是淘宝后台利用 J_HToken 以及其他数据经过计算之后得到的,可以利用 st 和用户名直接用 get 方式登录,所以 st 可以理解为一个秘钥。这个 st 值只会使用一次,如果第二次用 get 方式登录则会失效。所以它是一次性使用的。 下面 J_HToken 计算 st 的方法如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #通过token获得st
    def getSTbyToken(self,token):
    tokenURL = 'https://passport.alipay.com/mini_apply_st.js?site=0&token=%s&callback=stCallback6' % token
    request = urllib2.Request(tokenURL)
    response = urllib2.urlopen(request)
    #处理st,获得用户淘宝主页的登录地址
    pattern = re.compile('{"st":"(.*?)"}',re.S)
    result = re.search(pattern,response.read())
    #如果成功匹配
    if result:
    print u"成功获取st码"
    #获取st的值
    st = result.group(1)
    return st
    else:
    print u"未匹配到st"
    return False

    直接利用 st 登录

    得到 st 之后,基本上就大功告成啦,一段辛苦终于没有白费,你可以直接构建 get 方式请求的 URL,直接访问这个 URL 便可以实现登录。

    1
    stURL = 'https://login.taobao.com/member/vst.htm?st=%s&TPL_username=%s' % (st,username)

    比如

    1
     https://login.taobao.com/member/vst.htm?st=1uynJELa4hKfsfWU3OjPJCw&TPL_username=cqcre

    直接访问该链接即可实现登录,不过我这个应该已经失效了吧~ 代码在这先不贴了,剩下的一起贴了~

    获取已买到的宝贝页面

    已买到的宝贝的页面地址是

    1
    http://buyer.trade.taobao.com/trade/itemlist/list_bought_items.htm

    另外还有页码的参数。 重新构建一个带有 cookie 的 opener,将上面的带有 st 的 URL 打开,保存它的 cookie,然后再利用这个 opener 打开已买到的宝贝的页面,你就会得到已买到的宝贝页面详情了。

    1
    2
    3
    4
    5
    6
    #获得已买到的宝贝页面
    def getGoodsPage(self,pageIndex):
    goodsURL = 'http://buyer.trade.taobao.com/trade/itemlist/listBoughtItems.htm?action=itemlist/QueryAction&event_submit_do_query=1&pageNum=' + str(pageIndex)
    response = self.newOpener.open(goodsURL)
    page = response.read().decode('gbk')
    return page

    正则表达式提取信息 这是我的已买到的宝贝界面,审查元素可以看到,每一个宝贝都是 tbody 标签包围着。 QQ截图20150225223302我们现在想获取订单时间,订单号,卖家店铺名称,宝贝名称,原价,购买数量,最后付款多少,交易状态这几个量,具体就不再分析啦,正则表达式还不熟悉的同学请参考前面所说的正则表达式的用法,在这里,正则表达式匹配的代码是

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #u'\u8ba2\u5355\u53f7'是订单号的编码
    pattern = re.compile(u'dealtime.*?>(.*?)</span>.*?\u8ba2\u5355\u53f7.*?<em>(.*?)</em>.*?shopname.*?title="(.*?)".*?baobei-name">.*?<a.*?>(.*?)</a>.*?'
    u'price.*?title="(.*?)".*?quantity.*?title="(.*?)".*?amount.*?em.*?>(.*?)</em>.*?trade-status.*?<a.*?>(.*?)</a>',re.S)
    result = re.findall(pattern,page)
    for item in result:
    print '------------------------------------------------------------'
    print "购买日期:",item[0].strip(), '订单号:',item[1].strip(),'卖家店铺:',item[2].strip()
    print '宝贝名称:',item[3].strip()
    print '原价:',item[4].strip(),'购买数量:',item[5].strip(),'实际支付:',item[6].strip(),'交易状态',item[7].strip()

    最终代码整理

    恩,你懂得,最重要的东西来了,经过博主 2 天多的奋战,代码基本就构建完成。写了两个类,其中提取页面信息的方法我单独放到了一个类中,叫 tool.py,类名为 Tool。 先看一下运行结果吧~ QQ截图20150225234414 最终代码如下

    1
    tool.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-

    import re

    #处理获得的宝贝页面
    class Tool:

    #初始化
    def __init__(self):
    pass


    #获得页码数
    def getPageNum(self,page):
    pattern = re.compile(u'<div class="total">.*?\u5171(.*?)\u9875',re.S)
    result = re.search(pattern,page)
    if result:
    print "找到了共多少页"
    pageNum = result.group(1).strip()
    print '共',pageNum,'页'
    return pageNum

    def getGoodsInfo(self,page):
    #u'\u8ba2\u5355\u53f7'是订单号的编码
    pattern = re.compile(u'dealtime.*?>(.*?)</span>.*?\u8ba2\u5355\u53f7.*?<em>(.*?)</em>.*?shopname.*?title="(.*?)".*?baobei-name">.*?<a.*?>(.*?)</a>.*?'
    u'price.*?title="(.*?)".*?quantity.*?title="(.*?)".*?amount.*?em.*?>(.*?)</em>.*?trade-status.*?<a.*?>(.*?)</a>',re.S)
    result = re.findall(pattern,page)
    for item in result:
    print '------------------------------------------------------------'
    print "购买日期:",item[0].strip(), '订单号:',item[1].strip(),'卖家店铺:',item[2].strip()
    print '宝贝名称:',item[3].strip()
    print '原价:',item[4].strip(),'购买数量:',item[5].strip(),'实际支付:',item[6].strip(),'交易状态',item[7].strip()
    1
    taobao.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-

    import urllib
    import urllib2
    import cookielib
    import re
    import webbrowser
    import tool

    #模拟登录淘宝类
    class Taobao:

    #初始化方法
    def __init__(self):
    #登录的URL
    self.loginURL = "https://login.taobao.com/member/login.jhtml"
    #代理IP地址,防止自己的IP被封禁
    self.proxyURL = 'http://120.193.146.97:843'
    #登录POST数据时发送的头部信息
    self.loginHeaders = {
    'Host':'login.taobao.com',
    'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0',
    'Referer' : 'https://login.taobao.com/member/login.jhtml',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Connection' : 'Keep-Alive'
    }
    #用户名
    self.username = 'cqcre'
    #ua字符串,经过淘宝ua算法计算得出,包含了时间戳,浏览器,屏幕分辨率,随机数,鼠标移动,鼠标点击,其实还有键盘输入记录,鼠标移动的记录、点击的记录等等的信息
    self.ua = '191UW5TcyMNYQwiAiwTR3tCf0J/QnhEcUpkMmQ=|Um5Ockt0TXdPc011TXVKdyE=|U2xMHDJ+H2QJZwBxX39Rb1d5WXcrSixAJ1kjDVsN|VGhXd1llXGNaYFhkWmJaYl1gV2pIdUtyTXRKfkN4Qn1FeEF6R31TBQ==|VWldfS0TMw8xDjYWKhAwHiUdOA9wCDEVaxgkATdcNU8iDFoM|VmNDbUMV|V2NDbUMV|WGRYeCgGZhtmH2VScVI2UT5fORtmD2gCawwuRSJHZAFsCWMOdVYyVTpbPR99HWAFYVMpUDUFORshHiQdJR0jAT0JPQc/BDoFPgooFDZtVBR5Fn9VOwt2EWhCOVQ4WSJPJFkHXhgoSDVIMRgnHyFqQ3xEezceIRkmahRqFDZLIkUvRiEDaA9qQ3xEezcZORc5bzk=|WWdHFy0TMw8vEy0UIQE0ADgYJBohGjoAOw4uEiwXLAw2DThuOA==|WmBAED5+KnIbdRh1GXgFQSZbGFdrUm1UblZqVGxQa1ZiTGxQcEp1I3U=|W2NDEz19KXENZwJjHkY7Ui9OJQsre09zSWlXY1oMLBExHzERLxsuE0UT|XGZGFjh4LHQdcx5zH34DRyBdHlFtVGtSaFBsUmpWbVBkSmpXd05zTnMlcw==|XWdHFzl5LXUJYwZnGkI/VitKIQ8vEzMKNws3YTc=|XmdaZ0d6WmVFeUB8XGJaYEB4TGxWbk5yTndXa0tyT29Ta0t1QGBeZDI='
    #密码,在这里不能输入真实密码,淘宝对此密码进行了加密处理,256位,此处为加密后的密码
    self.password2 = '7511aa6854629e45de220d29174f1066537a73420ef6dbb5b46f202396703a2d56b0312df8769d886e6ca63d587fdbb99ee73927e8c07d9c88cd02182e1a21edc13fb8e140a4a2a4b53bf38484bd0e08199e03eb9bf7b365a5c673c03407d812b91394f0d3c7564042e3f2b11d156aeea37ad6460118914125ab8f8ac466f'
    self.post = post = {
    'ua':self.ua,
    'TPL_checkcode':'',
    'CtrlVersion': '1,0,0,7',
    'TPL_password':'',
    'TPL_redirect_url':'http://i.taobao.com/my_taobao.htm?nekot=udm8087E1424147022443',
    'TPL_username':self.username,
    'loginsite':'0',
    'newlogin':'0',
    'from':'tb',
    'fc':'default',
    'style':'default',
    'css_style':'',
    'tid':'XOR_1_000000000000000000000000000000_625C4720470A0A050976770A',
    'support':'000001',
    'loginType':'4',
    'minititle':'',
    'minipara':'',
    'umto':'NaN',
    'pstrong':'3',
    'llnick':'',
    'sign':'',
    'need_sign':'',
    'isIgnore':'',
    'full_redirect':'',
    'popid':'',
    'callback':'',
    'guf':'',
    'not_duplite_str':'',
    'need_user_id':'',
    'poy':'',
    'gvfdcname':'10',
    'gvfdcre':'',
    'from_encoding ':'',
    'sub':'',
    'TPL_password_2':self.password2,
    'loginASR':'1',
    'loginASRSuc':'1',
    'allp':'',
    'oslanguage':'zh-CN',
    'sr':'1366*768',
    'osVer':'windows|6.1',
    'naviVer':'firefox|35'
    }
    #将POST的数据进行编码转换
    self.postData = urllib.urlencode(self.post)
    #设置代理
    self.proxy = urllib2.ProxyHandler({'http':self.proxyURL})
    #设置cookie
    self.cookie = cookielib.LWPCookieJar()
    #设置cookie处理器
    self.cookieHandler = urllib2.HTTPCookieProcessor(self.cookie)
    #设置登录时用到的opener,它的open方法相当于urllib2.urlopen
    self.opener = urllib2.build_opener(self.cookieHandler,self.proxy,urllib2.HTTPHandler)
    #赋值J_HToken
    self.J_HToken = ''
    #登录成功时,需要的Cookie
    self.newCookie = cookielib.CookieJar()
    #登陆成功时,需要的一个新的opener
    self.newOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.newCookie))
    #引入工具类
    self.tool = tool.Tool()


    #得到是否需要输入验证码,这次请求的相应有时会不同,有时需要验证有时不需要
    def needCheckCode(self):
    #第一次登录获取验证码尝试,构建request
    request = urllib2.Request(self.loginURL,self.postData,self.loginHeaders)
    #得到第一次登录尝试的相应
    response = self.opener.open(request)
    #获取其中的内容
    content = response.read().decode('gbk')
    #获取状态吗
    status = response.getcode()
    #状态码为200,获取成功
    if status == 200:
    print u"获取请求成功"
    #\u8bf7\u8f93\u5165\u9a8c\u8bc1\u7801这六个字是请输入验证码的utf-8编码
    pattern = re.compile(u'\u8bf7\u8f93\u5165\u9a8c\u8bc1\u7801',re.S)
    result = re.search(pattern,content)
    #如果找到该字符,代表需要输入验证码
    if result:
    print u"此次安全验证异常,您需要输入验证码"
    return content
    #否则不需要
    else:
    #返回结果直接带有J_HToken字样,表明直接验证通过
    tokenPattern = re.compile('id="J_HToken" value="(.*?)"')
    tokenMatch = re.search(tokenPattern,content)
    if tokenMatch:
    self.J_HToken = tokenMatch.group(1)
    print u"此次安全验证通过,您这次不需要输入验证码"
    return False
    else:
    print u"获取请求失败"
    return None

    #得到验证码图片
    def getCheckCode(self,page):
    #得到验证码的图片
    pattern = re.compile('<img id="J_StandardCode_m.*?data-src="(.*?)"',re.S)
    #匹配的结果
    matchResult = re.search(pattern,page)
    #已经匹配得到内容,并且验证码图片链接不为空
    if matchResult and matchResult.group(1):
    return matchResult.group(1)
    else:
    print u"没有找到验证码内容"
    return False


    #输入验证码,重新请求,如果验证成功,则返回J_HToken
    def loginWithCheckCode(self):
    #提示用户输入验证码
    checkcode = raw_input('请输入验证码:')
    #将验证码重新添加到post的数据中
    self.post['TPL_checkcode'] = checkcode
    #对post数据重新进行编码
    self.postData = urllib.urlencode(self.post)
    try:
    #再次构建请求,加入验证码之后的第二次登录尝试
    request = urllib2.Request(self.loginURL,self.postData,self.loginHeaders)
    #得到第一次登录尝试的相应
    response = self.opener.open(request)
    #获取其中的内容
    content = response.read().decode('gbk')
    #检测验证码错误的正则表达式,\u9a8c\u8bc1\u7801\u9519\u8bef 是验证码错误五个字的编码
    pattern = re.compile(u'\u9a8c\u8bc1\u7801\u9519\u8bef',re.S)
    result = re.search(pattern,content)
    #如果返回页面包括了,验证码错误五个字
    if result:
    print u"验证码输入错误"
    return False
    else:
    #返回结果直接带有J_HToken字样,说明验证码输入成功,成功跳转到了获取HToken的界面
    tokenPattern = re.compile('id="J_HToken" value="(.*?)"')
    tokenMatch = re.search(tokenPattern,content)
    #如果匹配成功,找到了J_HToken
    if tokenMatch:
    print u"验证码输入正确"
    self.J_HToken = tokenMatch.group(1)
    return tokenMatch.group(1)
    else:
    #匹配失败,J_Token获取失败
    print u"J_Token获取失败"
    return False
    except urllib2.HTTPError, e:
    print u"连接服务器出错,错误原因",e.reason
    return False


    #通过token获得st
    def getSTbyToken(self,token):
    tokenURL = 'https://passport.alipay.com/mini_apply_st.js?site=0&token=%s&callback=stCallback6' % token
    request = urllib2.Request(tokenURL)
    response = urllib2.urlopen(request)
    #处理st,获得用户淘宝主页的登录地址
    pattern = re.compile('{"st":"(.*?)"}',re.S)
    result = re.search(pattern,response.read())
    #如果成功匹配
    if result:
    print u"成功获取st码"
    #获取st的值
    st = result.group(1)
    return st
    else:
    print u"未匹配到st"
    return False

    #利用st码进行登录,获取重定向网址
    def loginByST(self,st,username):
    stURL = 'https://login.taobao.com/member/vst.htm?st=%s&TPL_username=%s' % (st,username)
    headers = {
    'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0',
    'Host':'login.taobao.com',
    'Connection' : 'Keep-Alive'
    }
    request = urllib2.Request(stURL,headers = headers)
    response = self.newOpener.open(request)
    content = response.read().decode('gbk')
    #检测结果,看是否登录成功
    pattern = re.compile('top.location = "(.*?)"',re.S)
    match = re.search(pattern,content)
    if match:
    print u"登录网址成功"
    location = match.group(1)
    return True
    else:
    print "登录失败"
    return False


    #获得已买到的宝贝页面
    def getGoodsPage(self,pageIndex):
    goodsURL = 'http://buyer.trade.taobao.com/trade/itemlist/listBoughtItems.htm?action=itemlist/QueryAction&event_submit_do_query=1' + '&pageNum=' + str(pageIndex)
    response = self.newOpener.open(goodsURL)
    page = response.read().decode('gbk')
    return page

    #获取所有已买到的宝贝信息
    def getAllGoods(self,pageNum):
    print u"获取到的商品列表如下"
    for x in range(1,int(pageNum)+1):
    page = self.getGoodsPage(x)
    self.tool.getGoodsInfo(page)



    #程序运行主干
    def main(self):
    #是否需要验证码,是则得到页面内容,不是则返回False
    needResult = self.needCheckCode()
    #请求获取失败,得到的结果是None
    if not needResult ==None:
    if not needResult == False:
    print u"您需要手动输入验证码"
    checkCode = self.getCheckCode(needResult)
    #得到了验证码的链接
    if not checkCode == False:
    print u"验证码获取成功"
    print u"请在浏览器中输入您看到的验证码"
    webbrowser.open_new_tab(checkCode)
    self.loginWithCheckCode()
    #验证码链接为空,无效验证码
    else:
    print u"验证码获取失败,请重试"
    else:
    print u"不需要输入验证码"
    else:
    print u"请求登录页面失败,无法确认是否需要验证码"


    #判断token是否正常获取到
    if not self.J_HToken:
    print "获取Token失败,请重试"
    return
    #获取st码
    st = self.getSTbyToken(self.J_HToken)
    #利用st进行登录
    result = self.loginByST(st,self.username)
    if result:
    #获得所有宝贝的页面
    page = self.getGoodsPage(1)
    pageNum = self.tool.getPageNum(page)
    self.getAllGoods(pageNum)
    else:
    print u"登录失败"



    taobao = Taobao()
    taobao.main()

    好啦,运行结果就是上面贴的图片,可以成功获取到自己的商品列表,前提是把你们的 用户名,ua,password2 这三个设置好。 以上均为博主亲身所敲,代码写的不好,谨在此贴出和大家一起分享经验~ 小伙伴们试一下吧,希望对大家有帮助~

    PHP

    最近发现 WordPress 博客在修改 style.css 样式不能立即生效的问题,根本原因在于服务器开启了.htaccess 缓存,不过你总不能直接把这个关掉吧。开启.htaccess 缓存还是有很多好处的。

    我们为什么要设置.htaccess 缓存?

    网站一般不容易变化的都是一些图片,CSS,JS 脚本这些可以缓存到本地,设置一个缓存时间,比如 30 天,这样访客打开你的网站就不会在从网站服务器直接下载这些数据了,而是直接从本地缓存读取这些数据,这样就大大提高了网站加载速度,减少了加载时间。

    在不修改.htaccess 缓存情况下怎样实现修改 style.css 并且即时生效?

    经过博主这么多天的探索,总结出了一个”三步走”战略。

    第一步

    当然是修改主题下的 style.css 文件,去定制你想要的样式。

    第二步

    找到 functions.php 文件,找到类似

    1
    wp_register_style( 'style', get_template_directory_uri().'/style.css',false,25);

    这样的代码,这就是加载 style.css 文件的代码,在这里注意最后一个参数,这是一个版本号,在有了这个之后,浏览器中审查元素,你会发现 引入的样式文件带有一个参数,比如,现在我的引入的 css 文件就是这样子的。因为带有版本号,所以它缓存了这个文件,如果版本号不变更,那么它永远在加载这个版本的 CSS 文件。 所以,我们的解决方法就是改变后面的这个数字,也就是上述代码的最后一个参数。 修改完 style.css 文件之后,将这个数字加一即可。 P.S 如果你修改为之前的数字,那么它可能会加载回曾经的版本,在这里,最好修改一次递增一下这个数字。

    第三步

    现在,你刷新网页可能还不会即时生效样式,就找到 WP Super Cache 插件,点击删除缓存,清一下缓存即可。 20150223141547 如果没有安装这个插件的童鞋,请安装一个吧,很有用的缓存加速插件。 好,以上就是三步走战略来解决修改 style.css 不生效问题! 另外,如果你没有修改 style.css 文件,而是单纯在后台设置了一些选项而发现没有生效,只执行第三部即可生效,亲测可用!

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    Python2 爬虫系列教程

    以下为原 Python2 爬虫系列教程。

    大家好哈,我呢最近在学习 Python 爬虫,感觉非常有意思,真的让生活可以方便很多。学习过程中我把一些学习的笔记总结下来,还记录了一些自己实际写的一些小爬虫,在这里跟大家一同分享,希望对 Python 爬虫感兴趣的童鞋有帮助,如果有机会期待与大家的交流。

    一、爬虫入门

    1. Python 爬虫入门一之综述
    2. Python 爬虫入门二之爬虫基础了解
    3. Python 爬虫入门三之 Urllib 库的基本使用
    4. Python 爬虫入门四之 Urllib 库的高级用法
    5. Python 爬虫入门五之 URLError 异常处理
    6. Python 爬虫入门六之 Cookie 的使用
    7. Python 爬虫入门七之正则表达式

    二、爬虫实战

    1. Python 爬虫实战一之爬取糗事百科段子
    2. Python 爬虫实战二之爬取百度贴吧帖子
    3. Python 爬虫实战三之实现山东大学无线网络掉线自动重连
    4. Python 爬虫实战四之抓取淘宝 MM 照片
    5. Python 爬虫实战五之模拟登录淘宝并获取所有订单
    6. Python 爬虫实战六之抓取爱问知识人问题并保存至数据库
    7. Python 爬虫实战七之计算大学本学期绩点
    8. Python 爬虫实战八之利用 Selenium 抓取淘宝匿名旺旺

    三、爬虫利器

    1. Python 爬虫利器一之 Requests 库的用法
    2. Python 爬虫利器二之 Beautiful Soup 的用法
    3. Python 爬虫利器三之 Xpath 语法与 lxml 库的用法
    4. Python 爬虫利器四之 PhantomJS 的用法
    5. Python 爬虫利器五之 Selenium 的用法
    6. Python 爬虫利器六之 PyQuery 的用法

    四、爬虫进阶

    1. Python 爬虫进阶一之爬虫框架概述
    2. Python 爬虫进阶二之 PySpider 框架安装配置
    3. Python 爬虫进阶三之爬虫框架 Scrapy 安装配置
    4. Python 爬虫进阶四之 PySpider 的用法
    5. Python 爬虫进阶五之多线程的用法
    6. Python 爬虫进阶六之多进程的用法
    7. Python 爬虫进阶七之设置 ADSL 拨号服务器代理

    目前暂时是这些文章,随着学习的进行,会不断更新哒,敬请期待~ 希望对大家有所帮助,谢谢!

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    如下为原文。

    福利啊福利,本次为大家带来的项目是抓取淘宝 MM 照片并保存起来,大家有没有很激动呢?

    最新动态

    更新时间:2015/8/2 最近好多读者反映代码已经不能用了,原因是淘宝索引页的 MM 链接改了。网站改版了,URL 的索引已经和之前的不一样了,之前可以直接跳转到每个 MM 的个性域名,现在中间加了一个跳转页,本以为可以通过这个页面然后跳转到原来的个性域名,而经过一番折腾发现,这个跳转页中的内容是 JS 动态生成的,所以不能用 Urllib 库来直接抓取了,本篇就只提供学习思路,代码不能继续用了。 之后博主会利用其它方法来尝试解决,如果解决,第一时间更新!谢谢大家!

    更新时间:2016/3/26 如上问题已解决,利用 PhantomJS 的动态解析即可完成。因为 PySpider 同样支持 PhantomJS,所以我直接利用了 PySpider 来完成,解决方案如下 解决方案 另外如果不想使用框架,可以直接利用 Selenium + PhantomJS 来解析,同样方便,解决方案可以参考 动态解析解决方案

    本篇目标

    1.抓取淘宝 MM 的姓名,头像,年龄 2.抓取每一个 MM 的资料简介以及写真图片 3.把每一个 MM 的写真图片按照文件夹保存到本地 4.熟悉文件保存的过程

    1.URL 的格式

    在这里我们用到的 URL 是 http://mm.taobao.com/json/request_top_list.htm?page=1,问号前面是基地址,后面的参数 page 是代表第几页,可以随意更换地址。点击开之后,会发现有一些淘宝 MM 的简介,并附有超链接链接到个人详情页面。 我们需要抓取本页面的头像地址,MM 姓名,MM 年龄,MM 居住地,以及 MM 的个人详情页面地址。

    2.抓取简要信息

    相信大家经过上几次的实战,对抓取和提取页面的地址已经非常熟悉了,这里没有什么难度了,我们首先抓取本页面的 MM 详情页面地址,姓名,年龄等等的信息打印出来,直接贴代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-

    import urllib
    import urllib2
    import re

    class Spider:

    def __init__(self):
    self.siteURL = 'http://mm.taobao.com/json/request_top_list.htm'

    def getPage(self,pageIndex):
    url = self.siteURL + "?page=" + str(pageIndex)
    print url
    request = urllib2.Request(url)
    response = urllib2.urlopen(request)
    return response.read().decode('gbk')

    def getContents(self,pageIndex):
    page = self.getPage(pageIndex)
    pattern = re.compile('<div class="list-item".*?pic-word.*?<a href="(.*?)".*?<img src="(.*?)".*?<a class="lady-name.*?>(.*?)</a>.*?<strong>(.*?)</strong>.*?<span>(.*?)</span>',re.S)
    items = re.findall(pattern,page)
    for item in items:
    print item[0],item[1],item[2],item[3],item[4]

    spider = Spider()
    spider.getContents(1)

    运行结果如下 QQ截图20150220234132

    2.文件写入简介

    在这里,我们有写入图片和写入文本两种方式

    1)写入图片

    1
    2
    3
    4
    5
    6
    7
    #传入图片地址,文件名,保存单张图片
    def saveImg(self,imageURL,fileName):
    u = urllib.urlopen(imageURL)
    data = u.read()
    f = open(fileName, 'wb')
    f.write(data)
    f.close()

    2)写入文本

    1
    2
    3
    4
    5
    def saveBrief(self,content,name):
    fileName = name + "/" + name + ".txt"
    f = open(fileName,"w+")
    print u"正在偷偷保存她的个人信息为",fileName
    f.write(content.encode('utf-8'))

    3)创建新目录

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #创建新目录
    def mkdir(self,path):
    path = path.strip()
    # 判断路径是否存在
    # 存在 True
    # 不存在 False
    isExists=os.path.exists(path)
    # 判断结果
    if not isExists:
    # 如果不存在则创建目录
    # 创建目录操作函数
    os.makedirs(path)
    return True
    else:
    # 如果目录存在则不创建,并提示目录已存在
    return False

    3.代码完善

    主要的知识点已经在前面都涉及到了,如果大家前面的章节都已经看了,完成这个爬虫不在话下,具体的详情在此不再赘述,直接帖代码啦。

    1
    spider.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-

    import urllib
    import urllib2
    import re
    import tool
    import os

    #抓取MM
    class Spider:

    #页面初始化
    def __init__(self):
    self.siteURL = 'http://mm.taobao.com/json/request_top_list.htm'
    self.tool = tool.Tool()

    #获取索引页面的内容
    def getPage(self,pageIndex):
    url = self.siteURL + "?page=" + str(pageIndex)
    request = urllib2.Request(url)
    response = urllib2.urlopen(request)
    return response.read().decode('gbk')

    #获取索引界面所有MM的信息,list格式
    def getContents(self,pageIndex):
    page = self.getPage(pageIndex)
    pattern = re.compile('<div class="list-item".*?pic-word.*?<a href="(.*?)".*?<img src="(.*?)".*?<a class="lady-name.*?>(.*?)</a>.*?<strong>(.*?)</strong>.*?<span>(.*?)</span>',re.S)
    items = re.findall(pattern,page)
    contents = []
    for item in items:
    contents.append([item[0],item[1],item[2],item[3],item[4]])
    return contents

    #获取MM个人详情页面
    def getDetailPage(self,infoURL):
    response = urllib2.urlopen(infoURL)
    return response.read().decode('gbk')

    #获取个人文字简介
    def getBrief(self,page):
    pattern = re.compile('<div class="mm-aixiu-content".*?>(.*?)<!--',re.S)
    result = re.search(pattern,page)
    return self.tool.replace(result.group(1))

    #获取页面所有图片
    def getAllImg(self,page):
    pattern = re.compile('<div class="mm-aixiu-content".*?>(.*?)<!--',re.S)
    #个人信息页面所有代码
    content = re.search(pattern,page)
    #从代码中提取图片
    patternImg = re.compile('<img.*?src="(.*?)"',re.S)
    images = re.findall(patternImg,content.group(1))
    return images


    #保存多张写真图片
    def saveImgs(self,images,name):
    number = 1
    print u"发现",name,u"共有",len(images),u"张照片"
    for imageURL in images:
    splitPath = imageURL.split('.')
    fTail = splitPath.pop()
    if len(fTail) > 3:
    fTail = "jpg"
    fileName = name + "/" + str(number) + "." + fTail
    self.saveImg(imageURL,fileName)
    number += 1

    # 保存头像
    def saveIcon(self,iconURL,name):
    splitPath = iconURL.split('.')
    fTail = splitPath.pop()
    fileName = name + "/icon." + fTail
    self.saveImg(iconURL,fileName)

    #保存个人简介
    def saveBrief(self,content,name):
    fileName = name + "/" + name + ".txt"
    f = open(fileName,"w+")
    print u"正在偷偷保存她的个人信息为",fileName
    f.write(content.encode('utf-8'))


    #传入图片地址,文件名,保存单张图片
    def saveImg(self,imageURL,fileName):
    u = urllib.urlopen(imageURL)
    data = u.read()
    f = open(fileName, 'wb')
    f.write(data)
    print u"正在悄悄保存她的一张图片为",fileName
    f.close()

    #创建新目录
    def mkdir(self,path):
    path = path.strip()
    # 判断路径是否存在
    # 存在 True
    # 不存在 False
    isExists=os.path.exists(path)
    # 判断结果
    if not isExists:
    # 如果不存在则创建目录
    print u"偷偷新建了名字叫做",path,u'的文件夹'
    # 创建目录操作函数
    os.makedirs(path)
    return True
    else:
    # 如果目录存在则不创建,并提示目录已存在
    print u"名为",path,'的文件夹已经创建成功'
    return False

    #将一页淘宝MM的信息保存起来
    def savePageInfo(self,pageIndex):
    #获取第一页淘宝MM列表
    contents = self.getContents(pageIndex)
    for item in contents:
    #item[0]个人详情URL,item[1]头像URL,item[2]姓名,item[3]年龄,item[4]居住地
    print u"发现一位模特,名字叫",item[2],u"芳龄",item[3],u",她在",item[4]
    print u"正在偷偷地保存",item[2],"的信息"
    print u"又意外地发现她的个人地址是",item[0]
    #个人详情页面的URL
    detailURL = item[0]
    #得到个人详情页面代码
    detailPage = self.getDetailPage(detailURL)
    #获取个人简介
    brief = self.getBrief(detailPage)
    #获取所有图片列表
    images = self.getAllImg(detailPage)
    self.mkdir(item[2])
    #保存个人简介
    self.saveBrief(brief,item[2])
    #保存头像
    self.saveIcon(item[1],item[2])
    #保存图片
    self.saveImgs(images,item[2])

    #传入起止页码,获取MM图片
    def savePagesInfo(self,start,end):
    for i in range(start,end+1):
    print u"正在偷偷寻找第",i,u"个地方,看看MM们在不在"
    self.savePageInfo(i)


    #传入起止页码即可,在此传入了2,10,表示抓取第2到10页的MM
    spider = Spider()
    spider.savePagesInfo(2,10)
    1
    tool.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    __author__ = 'CQC'
    #-*- coding:utf-8 -*-
    import re

    #处理页面标签类
    class Tool:
    #去除img标签,1-7位空格,&nbsp;
    removeImg = re.compile('<img.*?>| {1,7}|&nbsp;')
    #删除超链接标签
    removeAddr = re.compile('<a.*?>|</a>')
    #把换行的标签换为\n
    replaceLine = re.compile('<tr>|<div>|</div>|</p>')
    #将表格制表<td>替换为\t
    replaceTD= re.compile('<td>')
    #将换行符或双换行符替换为\n
    replaceBR = re.compile('<br><br>|<br>')
    #将其余标签剔除
    removeExtraTag = re.compile('<.*?>')
    #将多行空行删除
    removeNoneLine = re.compile('\n+')
    def replace(self,x):
    x = re.sub(self.removeImg,"",x)
    x = re.sub(self.removeAddr,"",x)
    x = re.sub(self.replaceLine,"\n",x)
    x = re.sub(self.replaceTD,"\t",x)
    x = re.sub(self.replaceBR,"\n",x)
    x = re.sub(self.removeExtraTag,"",x)
    x = re.sub(self.removeNoneLine,"\n",x)
    #strip()将前后多余内容删除
    return x.strip()

    以上两个文件就是所有的代码内容,运行一下试试看,那叫一个酸爽啊 QQ截图20150221020543 看看文件夹里面有什么变化 QQ截图20150221020709 QQ截图20150221021032 不知不觉,海量的 MM 图片已经进入了你的电脑,还不快快去试试看!! 代码均为本人所敲,写的不好,大神勿喷,写来方便自己,同时分享给大家参考!希望大家支持!

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    如下为原文。

    大家好,本次为大家带来的项目是计算大学本学期绩点。首先说明的是,博主来自山东大学,有属于个人的学生成绩管理系统,需要学号密码才可以登录,不过可能广大读者没有这个学号密码,不能实际进行操作,所以最主要的还是获取它的原理。最主要的是了解 cookie 的相关操作。

    本篇目标

    1.模拟登录学生成绩管理系统 2.抓取本学期成绩界面 3.计算打印本学期成绩

    1.URL 的获取

    恩,博主来自山东大学~ 先贴一个 URL,让大家知道我们学校学生信息系统的网站构架,主页是 http://jwxt.sdu.edu.cn:7890/zhxt_bks/zhxt_bks.html,山东大学学生个人信息系统,进去之后,Oh 不,他竟然用了 frame,一个多么古老的而又任性的写法,真是惊出一身冷汗~ 算了,就算他是 frame 又能拿我怎么样?我们点到登录界面,审查一下元素,先看看登录界面的 URL 是怎样的? QQ截图20150220211218 恩,看到了右侧的 frame 名称,src=”xk_login.html”,可以分析出完整的登录界面的网址为 http://jwxt.sdu.edu.cn:7890/zhxt_bks/xk_login.html,点进去看看,真是棒棒哒,他喵的竟然是清华大学选课系统,醉了,你说你抄袭就抄袭吧,改改名字也不错啊~ 算了,就不和他计较了。现在,我们登录一下,用浏览器监听网络。 我用的是猎豹浏览器,审查元素时会有一个网络的选项,如果大家用的 Chrome,也有相对应的功能,Firefox 需要装插件 HttpFox,同样可以实现。 这个网络监听功能可以监听表单的传送以及请求头,响应头等等的信息。截个图看一下,恩,我偷偷把密码隐藏了,你看不到~ 大家看到的是登录之后出现的信息以及 NetWork 监听,显示了 hearders 的详细信息。 QQ截图20150220212025 最主要的内容,我们可以发现有一个表单提交的过程,提交方式为 POST,两个参数分别为 stuid 和 pwd。 请求的 URL 为 http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login,没错,找到表单数据和目标地址就是这么简单。 在这里注意,刚才的 http://jwxt.sdu.edu.cn:7890/zhxt_bks/xk_login.html 只是登录界面的地址,刚刚得到的这个地址才是登录索要提交到的真正的 URL。希望大家这里不要混淆。 不知道山大这个系统有没有做 headers 的检查,我们先不管这么多,先尝试一下模拟登录并保存 Cookie。

    2.模拟登录

    好,通过以上信息,我们已经找到了登录的目标地址为 http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login 有一个表单提交到这个 URL,表单的两个内容分别为 stuid 和 pwd,学号和密码,没有其他的隐藏信息,提交方式为 POST。 好,现在我们首先构造以下代码来完成登录。看看会不会获取到登录之后的提示页面。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-

    import urllib
    import urllib2
    import cookielib
    import re

    #山东大学绩点运算
    class SDU:

    def __init__(self):
    self.loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
    self.cookies = cookielib.CookieJar()
    self.postdata = urllib.urlencode({
    'stuid':'201200131012',
    'pwd':'xxxxxx'
    })
    self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookies))

    def getPage(self):
    request = urllib2.Request(
    url = self.loginUrl,
    data = self.postdata)
    result = self.opener.open(request)
    #打印登录内容
    print result.read().decode('gbk')


    sdu = SDU()
    sdu.getPage()

    测试一下,竟然成功了,山大这网竟然没有做 headers 检查,很顺利就登录进去了。 说明一下,在这里我们利用了前面所说的 cookie,用到了 CookieJar 这个对象来保存 cookies,另外通过构建 opener,利用 open 方法实现了登录。如果大家觉得这里有疑惑,请看 Python 爬虫入门六之 Cookie 的使用,这篇文章说得比较详细。 好,我们看一下运行结果 QQ截图20150220214238 酸爽啊,接下来我们只要再获取到本学期成绩界面然后把成绩抓取出来就好了。

    3.抓取本学期成绩

    让我们先在浏览器中找到本学期成绩界面,点击左边的本学期成绩。 QQ截图20150220220000 重新审查元素,你会发现这个 frame 的 src 还是没有变,仍然是 xk_login.html,引起这个页面变化的原因是在左边的本学期成绩这个超链接设置了一个目标 frame,所以,那个页面就显示在右侧了。 所以,让我们再审查一下本学期成绩这个超链接的内容是什么~ QQ截图20150220220338 恩,找到它了,本学期成绩 那么,完整的 URL 就是 http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre,好,URL 已经找到了,我们继续完善一下代码,获取这个页面。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-

    import urllib
    import urllib2
    import cookielib
    import re

    #山东大学绩点运算
    class SDU:

    def __init__(self):
    #登录URL
    self.loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
    #本学期成绩URL
    self.gradeUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre'
    self.cookies = cookielib.CookieJar()
    self.postdata = urllib.urlencode({
    'stuid':'201200131012',
    'pwd':'xxxxxx'
    })
    #构建opener
    self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookies))

    #获取本学期成绩页面
    def getPage(self):
    request = urllib2.Request(
    url = self.loginUrl,
    data = self.postdata)
    result = self.opener.open(request)
    result = self.opener.open(self.gradeUrl)
    #打印登录内容
    print result.read().decode('gbk')


    sdu = SDU()
    sdu.getPage()

    上面的代码,我们最主要的是增加了

    1
    result = self.opener.open(self.gradeUrl)

    这句代码,用原来的 opener 访问一个本学期成绩的 URL 即可。运行结果如下 QQ截图20150220221909 恩,本学期成绩的页面已经被我们抓取下来了,接下来用正则表达式提取一下,然后计算学分即可

    4.抓取有效信息

    接下来我们就把页面内容提取一下,最主要的便是学分以及分数了。 平均绩点 = ∑(每科学分*每科分数)/总学分 所以我们把每科的学分以及分数抓取下来就好了,对于有些课打了良好或者优秀等级的,我们不进行抓取。 我们可以发现每一科都是 TR 标签,然后是一系列的 td 标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <TR>
    <td bgcolor="#EAE2F3"><p align="center"><INPUT TYPE="checkbox" NAME="p_pm" VALUE="013320131012015011294 面向对象技术"></p></td>
    <td bgcolor="#EAE2F3"><p align="center">0133201310</p></td>
    <td bgcolor="#EAE2F3"><p align="center">面向对象技术</p></td>
    <td bgcolor="#EAE2F3"><p align="center">1</p></td>
    <td bgcolor="#EAE2F3"><p align="center">2.5</p></td>
    <td bgcolor="#EAE2F3"><p align="center">20150112</p></td>
    <td bgcolor="#EAE2F3"><p align="center">94</p></td>
    <td bgcolor="#EAE2F3"><p align="center">必修</p></td>
    </TR>

    我们用下面的正则表达式进行提取即可,部分代码如下

    1
    2
    3
    4
    5
    page = self.getPage()
    myItems = re.findall('<TR>.*?<p.*?<p.*?<p.*?<p.*?<p.*?>(.*?)</p>.*?<p.*?<p.*?>(.*?)</p>.*?</TR>',page,re.S)
    for item in myItems:
    self.credit.append(item[0].encode('gbk'))
    self.grades.append(item[1].encode('gbk'))

    主要利用了 findall 方法,这个方法在此就不多介绍了,前面我们已经用过多次了。 得到的学分和分数我们都用列表 list 进行存储,所以用了 append 方法,每获取到一个信息就把它加进去。

    5.整理计算最后绩点

    恩,像上面那样把学分绩点都保存到列表 list 中了,所以我们最后用一个公式来计算学分绩点就好了,最后整理后的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    # -*- coding: utf-8 -*-

    import urllib
    import urllib2
    import cookielib
    import re
    import string

    #绩点运算
    class SDU:

    #类的初始化
    def __init__(self):
    #登录URL
    self.loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
    #成绩URL
    self.gradeUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre'
    #CookieJar对象
    self.cookies = cookielib.CookieJar()
    #表单数据
    self.postdata = urllib.urlencode({
    'stuid':'201200131012',
    'pwd':'xxxxx'
    })
    #构建opener
    self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.cookies))
    #学分list
    self.credit = []
    #成绩list
    self.grades = []

    def getPage(self):
    req = urllib2.Request(
    url = self.loginUrl,
    data = self.postdata)
    result = self.opener.open(req)
    result = self.opener.open(self.gradeUrl)
    #返回本学期成绩页面
    return result.read().decode('gbk')

    def getGrades(self):
    #获得本学期成绩页面
    page = self.getPage()
    #正则匹配
    myItems = re.findall('<TR>.*?<p.*?<p.*?<p.*?<p.*?<p.*?>(.*?)</p>.*?<p.*?<p.*?>(.*?)</p>.*?</TR>',page,re.S)
    for item in myItems:
    self.credit.append(item[0].encode('gbk'))
    self.grades.append(item[1].encode('gbk'))
    self.getGrade()

    def getGrade(self):
    #计算总绩点
    sum = 0.0
    weight = 0.0
    for i in range(len(self.credit)):
    if(self.grades[i].isdigit()):
    sum += string.atof(self.credit[i])*string.atof(self.grades[i])
    weight += string.atof(self.credit[i])

    print u"本学期绩点为:",sum/weight

    sdu = SDU()
    sdu.getGrades()

    好,最后就会打印输出本学期绩点是多少,小伙伴们最主要的了解上面的编程思路就好。 最主要的内容就是 Cookie 的使用,模拟登录的功能。 本文思路参考来源:汪海的爬虫 希望小伙伴们加油,加深一下理解。

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    如下为原文。

    大家好,上次我们实验了爬取了糗事百科的段子,那么这次我们来尝试一下爬取百度贴吧的帖子。与上一篇不同的是,这次我们需要用到文件的相关操作。

    前言

    亲爱的们,教程比较旧了,百度贴吧页面可能改版,可能代码不好使,八成是正则表达式那儿匹配不到了,请更改一下正则,当然最主要的还是帮助大家理解思路。

    2016/12/2

    本篇目标

    1.对百度贴吧的任意帖子进行抓取 2.指定是否只抓取楼主发帖内容 3.将抓取到的内容分析并保存到文件

    1.URL 格式的确定

    首先,我们先观察一下百度贴吧的任意一个帖子。 比如:http://tieba.baidu.com/p/3138733512?see_lz=1&pn=1,这是一个关于 NBA50 大的盘点,分析一下这个地址。

    1
    2
    3
    4
    http://  代表资源传输使用http协议
    tieba.baidu.com 是百度的二级域名,指向百度贴吧的服务器。
    /p/3138733512 是服务器某个资源,即这个帖子的地址定位符
    see_lz和pn是该URL的两个参数,分别代表了只看楼主和帖子页码,等于1表示该条件为真

    所以我们可以把 URL 分为两部分,一部分为基础部分,一部分为参数部分。 例如,上面的 URL 我们划分基础部分是 http://tieba.baidu.com/p/3138733512,参数部分是 ?see_lz=1&pn=1

    2.页面的抓取

    熟悉了 URL 的格式,那就让我们用 urllib2 库来试着抓取页面内容吧。上一篇糗事百科我们最后改成了面向对象的编码方式,这次我们直接尝试一下,定义一个类名叫 BDTB(百度贴吧),一个初始化方法,一个获取页面的方法。 其中,有些帖子我们想指定给程序是否要只看楼主,所以我们把只看楼主的参数初始化放在类的初始化上,即 init 方法。另外,获取页面的方法我们需要知道一个参数就是帖子页码,所以这个参数的指定我们放在该方法中。 综上,我们初步构建出基础代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-
    import urllib
    import urllib2
    import re

    #百度贴吧爬虫类
    class BDTB:

    #初始化,传入基地址,是否只看楼主的参数
    def __init__(self,baseUrl,seeLZ):
    self.baseURL = baseUrl
    self.seeLZ = '?see_lz='+str(seeLZ)

    #传入页码,获取该页帖子的代码
    def getPage(self,pageNum):
    try:
    url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
    request = urllib2.Request(url)
    response = urllib2.urlopen(request)
    print response.read()
    return response
    except urllib2.URLError, e:
    if hasattr(e,"reason"):
    print u"连接百度贴吧失败,错误原因",e.reason
    return None

    baseURL = 'http://tieba.baidu.com/p/3138733512'
    bdtb = BDTB(baseURL,1)
    bdtb.getPage(1)

    运行代码,我们可以看到屏幕上打印出了这个帖子第一页楼主发言的所有内容,形式为 HTML 代码。 20150219162232

    3.提取相关信息

    1)提取帖子标题

    首先,让我们提取帖子的标题。 在浏览器中审查元素,或者按 F12,查看页面源代码,我们找到标题所在的代码段,可以发现这个标题的 HTML 代码是

    1
    <h1 class="core_title_txt  " title="纯原创我心中的NBA2014-2015赛季现役50大" style="width: 396px">纯原创我心中的NBA2014-2015赛季现役50大</h1>

    所以我们想提取

    标签中的内容,同时还要指定这个 class 确定唯一,因为 h1 标签实在太多啦。 正则表达式如下

    1
    <h1 class="core_title_txt.*?>(.*?)</h1>

    所以,我们增加一个获取页面标题的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #获取帖子标题
    def getTitle(self):
    page = self.getPage(1)
    pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
    result = re.search(pattern,page)
    if result:
    #print result.group(1) #测试输出
    return result.group(1).strip()
    else:
    return None

    2)提取帖子页数

    同样地,帖子总页数我们也可以通过分析页面中的共?页来获取。所以我们的获取总页数的方法如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #获取帖子一共有多少页
    def getPageNum(self):
    page = self.getPage(1)
    pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
    result = re.search(pattern,page)
    if result:
    #print result.group(1) #测试输出
    return result.group(1).strip()
    else:
    return None

    3)提取正文内容

    审查元素,我们可以看到百度贴吧每一层楼的主要内容都在

    标签里面,所以我们可以写如下的正则表达式

    1
    <div id="post_content_.*?>(.*?)</div>

    相应地,获取页面所有楼层数据的方法可以写成如下方法

    1
    2
    3
    4
    5
    6
    #获取每一层楼的内容,传入页面内容
    def getContent(self,page):
    pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
    items = re.findall(pattern,page)
    for item in items:
    print item

    好,我们运行一下结果看一下 20150219235120 真是醉了,还有一大片换行符和图片符,好口怕!既然这样,我们就要对这些文本进行处理,把各种各样复杂的标签给它剔除掉,还原精华内容,把文本处理写成一个方法也可以,不过为了实现更好的代码架构和代码重用,我们可以考虑把标签等的处理写作一个类。 那我们就叫它 Tool(工具类吧),里面定义了一个方法,叫 replace,是替换各种标签的。在类中定义了几个正则表达式,主要利用了 re.sub 方法对文本进行匹配后然后替换。具体的思路已经写到注释中,大家可以看一下这个类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    import re

    #处理页面标签类
    class Tool:
    #去除img标签,7位长空格
    removeImg = re.compile('<img.*?>| {7}|')
    #删除超链接标签
    removeAddr = re.compile('<a.*?>|</a>')
    #把换行的标签换为\n
    replaceLine = re.compile('<tr>|<div>|</div>|</p>')
    #将表格制表<td>替换为\t
    replaceTD= re.compile('<td>')
    #把段落开头换为\n加空两格
    replacePara = re.compile('<p.*?>')
    #将换行符或双换行符替换为\n
    replaceBR = re.compile('<br><br>|<br>')
    #将其余标签剔除
    removeExtraTag = re.compile('<.*?>')
    def replace(self,x):
    x = re.sub(self.removeImg,"",x)
    x = re.sub(self.removeAddr,"",x)
    x = re.sub(self.replaceLine,"\n",x)
    x = re.sub(self.replaceTD,"\t",x)
    x = re.sub(self.replacePara,"\n ",x)
    x = re.sub(self.replaceBR,"\n",x)
    x = re.sub(self.removeExtraTag,"",x)
    #strip()将前后多余内容删除
    return x.strip()

    在使用时,我们只需要初始化一下这个类,然后调用 replace 方法即可。 现在整体代码是如下这样子的,现在我的代码是写到这样子的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-
    import urllib
    import urllib2
    import re

    #处理页面标签类
    class Tool:
    #去除img标签,7位长空格
    removeImg = re.compile('<img.*?>| {7}|')
    #删除超链接标签
    removeAddr = re.compile('<a.*?>|</a>')
    #把换行的标签换为\n
    replaceLine = re.compile('<tr>|<div>|</div>|</p>')
    #将表格制表<td>替换为\t
    replaceTD= re.compile('<td>')
    #把段落开头换为\n加空两格
    replacePara = re.compile('<p.*?>')
    #将换行符或双换行符替换为\n
    replaceBR = re.compile('<br><br>|<br>')
    #将其余标签剔除
    removeExtraTag = re.compile('<.*?>')
    def replace(self,x):
    x = re.sub(self.removeImg,"",x)
    x = re.sub(self.removeAddr,"",x)
    x = re.sub(self.replaceLine,"\n",x)
    x = re.sub(self.replaceTD,"\t",x)
    x = re.sub(self.replacePara,"\n ",x)
    x = re.sub(self.replaceBR,"\n",x)
    x = re.sub(self.removeExtraTag,"",x)
    #strip()将前后多余内容删除
    return x.strip()


    #百度贴吧爬虫类
    class BDTB:

    #初始化,传入基地址,是否只看楼主的参数
    def __init__(self,baseUrl,seeLZ):
    self.baseURL = baseUrl
    self.seeLZ = '?see_lz='+str(seeLZ)
    self.tool = Tool()
    #传入页码,获取该页帖子的代码
    def getPage(self,pageNum):
    try:
    url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
    request = urllib2.Request(url)
    response = urllib2.urlopen(request)
    return response.read().decode('utf-8')
    except urllib2.URLError, e:
    if hasattr(e,"reason"):
    print u"连接百度贴吧失败,错误原因",e.reason
    return None

    #获取帖子标题
    def getTitle(self):
    page = self.getPage(1)
    pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
    result = re.search(pattern,page)
    if result:
    #print result.group(1) #测试输出
    return result.group(1).strip()
    else:
    return None

    #获取帖子一共有多少页
    def getPageNum(self):
    page = self.getPage(1)
    pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
    result = re.search(pattern,page)
    if result:
    #print result.group(1) #测试输出
    return result.group(1).strip()
    else:
    return None

    #获取每一层楼的内容,传入页面内容
    def getContent(self,page):
    pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
    items = re.findall(pattern,page)
    #for item in items:
    # print item
    print self.tool.replace(items[1])


    baseURL = 'http://tieba.baidu.com/p/3138733512'
    bdtb = BDTB(baseURL,1)
    bdtb.getContent(bdtb.getPage(1))

    我们尝试一下,重新再看一下效果,这下经过处理之后应该就没问题了,是不是感觉好酸爽! 20150220000103

    4)替换楼层

    至于这个问题,我感觉直接提取楼层没什么必要呀,因为只看楼主的话,有些楼层的编号是间隔的,所以我们得到的楼层序号是不连续的,这样我们保存下来也没什么用。 所以可以尝试下面的方法:

    1.每打印输出一段楼层,写入一行横线来间隔,或者换行符也好。 2.试着重新编一个楼层,按照顺序,设置一个变量,每打印出一个结果变量加一,打印出这个变量当做楼层。

    这里我们尝试一下吧,看看效果怎样 把 getContent 方法修改如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #获取每一层楼的内容,传入页面内容
    def getContent(self,page):
    pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
    items = re.findall(pattern,page)
    floor = 1
    for item in items:
    print floor,u"楼------------------------------------------------------------------------------------------------------------------------------------\n"
    print self.tool.replace(item)
    floor += 1

    运行一下看看效果 20150220000947 嘿嘿,效果还不错吧,感觉真酸爽!接下来我们完善一下,然后写入文件

    4.写入文件

    最后便是写入文件的过程,过程很简单,就几句话的代码而已,主要是利用了以下两句

    file = open(“tb.txt”,”w”) file.writelines(obj)

    这里不再赘述,稍后直接贴上完善之后的代码。

    5.完善代码

    现在我们对代码进行优化,重构,在一些地方添加必要的打印信息,整理如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-
    import urllib
    import urllib2
    import re

    #处理页面标签类
    class Tool:
    #去除img标签,7位长空格
    removeImg = re.compile('<img.*?>| {7}|')
    #删除超链接标签
    removeAddr = re.compile('<a.*?>|</a>')
    #把换行的标签换为\n
    replaceLine = re.compile('<tr>|<div>|</div>|</p>')
    #将表格制表<td>替换为\t
    replaceTD= re.compile('<td>')
    #把段落开头换为\n加空两格
    replacePara = re.compile('<p.*?>')
    #将换行符或双换行符替换为\n
    replaceBR = re.compile('<br><br>|<br>')
    #将其余标签剔除
    removeExtraTag = re.compile('<.*?>')
    def replace(self,x):
    x = re.sub(self.removeImg,"",x)
    x = re.sub(self.removeAddr,"",x)
    x = re.sub(self.replaceLine,"\n",x)
    x = re.sub(self.replaceTD,"\t",x)
    x = re.sub(self.replacePara,"\n ",x)
    x = re.sub(self.replaceBR,"\n",x)
    x = re.sub(self.removeExtraTag,"",x)
    #strip()将前后多余内容删除
    return x.strip()


    #百度贴吧爬虫类
    class BDTB:

    #初始化,传入基地址,是否只看楼主的参数
    def __init__(self,baseUrl,seeLZ,floorTag):
    #base链接地址
    self.baseURL = baseUrl
    #是否只看楼主
    self.seeLZ = '?see_lz='+str(seeLZ)
    #HTML标签剔除工具类对象
    self.tool = Tool()
    #全局file变量,文件写入操作对象
    self.file = None
    #楼层标号,初始为1
    self.floor = 1
    #默认的标题,如果没有成功获取到标题的话则会用这个标题
    self.defaultTitle = u"百度贴吧"
    #是否写入楼分隔符的标记
    self.floorTag = floorTag

    #传入页码,获取该页帖子的代码
    def getPage(self,pageNum):
    try:
    #构建URL
    url = self.baseURL+ self.seeLZ + '&pn=' + str(pageNum)
    request = urllib2.Request(url)
    response = urllib2.urlopen(request)
    #返回UTF-8格式编码内容
    return response.read().decode('utf-8')
    #无法连接,报错
    except urllib2.URLError, e:
    if hasattr(e,"reason"):
    print u"连接百度贴吧失败,错误原因",e.reason
    return None

    #获取帖子标题
    def getTitle(self,page):
    #得到标题的正则表达式
    pattern = re.compile('<h1 class="core_title_txt.*?>(.*?)</h1>',re.S)
    result = re.search(pattern,page)
    if result:
    #如果存在,则返回标题
    return result.group(1).strip()
    else:
    return None

    #获取帖子一共有多少页
    def getPageNum(self,page):
    #获取帖子页数的正则表达式
    pattern = re.compile('<li class="l_reply_num.*?</span>.*?<span.*?>(.*?)</span>',re.S)
    result = re.search(pattern,page)
    if result:
    return result.group(1).strip()
    else:
    return None

    #获取每一层楼的内容,传入页面内容
    def getContent(self,page):
    #匹配所有楼层的内容
    pattern = re.compile('<div id="post_content_.*?>(.*?)</div>',re.S)
    items = re.findall(pattern,page)
    contents = []
    for item in items:
    #将文本进行去除标签处理,同时在前后加入换行符
    content = "\n"+self.tool.replace(item)+"\n"
    contents.append(content.encode('utf-8'))
    return contents

    def setFileTitle(self,title):
    #如果标题不是为None,即成功获取到标题
    if title is not None:
    self.file = open(title + ".txt","w+")
    else:
    self.file = open(self.defaultTitle + ".txt","w+")

    def writeData(self,contents):
    #向文件写入每一楼的信息
    for item in contents:
    if self.floorTag == '1':
    #楼之间的分隔符
    floorLine = "\n" + str(self.floor) + u"-----------------------------------------------------------------------------------------\n"
    self.file.write(floorLine)
    self.file.write(item)
    self.floor += 1

    def start(self):
    indexPage = self.getPage(1)
    pageNum = self.getPageNum(indexPage)
    title = self.getTitle(indexPage)
    self.setFileTitle(title)
    if pageNum == None:
    print "URL已失效,请重试"
    return
    try:
    print "该帖子共有" + str(pageNum) + "页"
    for i in range(1,int(pageNum)+1):
    print "正在写入第" + str(i) + "页数据"
    page = self.getPage(i)
    contents = self.getContent(page)
    self.writeData(contents)
    #出现写入异常
    except IOError,e:
    print "写入异常,原因" + e.message
    finally:
    print "写入任务完成"



    print u"请输入帖子代号"
    baseURL = 'http://tieba.baidu.com/p/' + str(raw_input(u'http://tieba.baidu.com/p/'))
    seeLZ = raw_input("是否只获取楼主发言,是输入1,否输入0\n")
    floorTag = raw_input("是否写入楼层信息,是输入1,否输入0\n")
    bdtb = BDTB(baseURL,seeLZ,floorTag)
    bdtb.start()

    现在程序演示如下 20150220012351 完成之后,可以查看一下当前目录下多了一个以该帖子命名的 txt 文件,内容便是帖子的所有数据。 抓贴吧,就是这么简单和任性!

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    原文

    大家好,前面入门已经说了那么多基础知识了,下面我们做几个实战项目来挑战一下吧。那么这次为大家带来,Python 爬取糗事百科的小段子的例子。 首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把,这次我们尝试一下用爬虫把他们抓取下来。

    友情提示

    糗事百科在前一段时间进行了改版,导致之前的代码没法用了,会导致无法输出和 CPU 占用过高的情况,是因为正则表达式没有匹配到的缘故。 现在,博主已经对程序进行了重新修改,代码亲测可用,包括截图和说明,之前一直在忙所以没有及时更新,望大家海涵! 更新时间:2015/8/2

    糗事百科又又又又改版了,博主已经没心再去一次次匹配它了,如果大家遇到长时间运行不出结果也不报错的情况,请大家参考最新的评论,热心小伙伴提供的正则来修改下吧~ 更新时间:2016/3/27

    本篇目标

    1.抓取糗事百科热门段子 2.过滤带有图片的段子 3.实现每按一次回车显示一个段子的发布时间,发布人,段子内容,点赞数。

    糗事百科是不需要登录的,所以也没必要用到 Cookie,另外糗事百科有的段子是附图的,我们把图抓下来图片不便于显示,那么我们就尝试过滤掉有图的段子吧。 好,现在我们尝试抓取一下糗事百科的热门段子吧,每按下一次回车我们显示一个段子。

    1.确定 URL 并抓取页面代码

    首先我们确定好页面的 URL 是 http://www.qiushibaike.com/hot/page/1,其中最后一个数字1代表页数,我们可以传入不同的值来获得某一页的段子内容。 我们初步构建如下的代码来打印页面代码内容试试看,先构造最基本的页面抓取方式,看看会不会成功

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # -*- coding:utf-8 -*-
    import urllib
    import urllib2


    page = 1
    url = 'http://www.qiushibaike.com/hot/page/' + str(page)
    try:
    request = urllib2.Request(url)
    response = urllib2.urlopen(request)
    print response.read()
    except urllib2.URLError, e:
    if hasattr(e,"code"):
    print e.code
    if hasattr(e,"reason"):
    print e.reason

    运行程序,哦不,它竟然报错了,真是时运不济,命途多舛啊

    1
    2
    3
    line 373, in _read_status
    raise BadStatusLine(line)
    httplib.BadStatusLine: ''

    好吧,应该是 headers 验证的问题,我们加上一个 headers 验证试试看吧,将代码修改如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # -*- coding:utf-8 -*-
    import urllib
    import urllib2

    page = 1
    url = 'http://www.qiushibaike.com/hot/page/' + str(page)
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers = { 'User-Agent' : user_agent }
    try:
    request = urllib2.Request(url,headers = headers)
    response = urllib2.urlopen(request)
    print response.read()
    except urllib2.URLError, e:
    if hasattr(e,"code"):
    print e.code
    if hasattr(e,"reason"):
    print e.reason

    嘿嘿,这次运行终于正常了,打印出了第一页的 HTML 代码,大家可以运行下代码试试看。在这里运行结果太长就不贴了。

    2.提取某一页的所有段子

    好,获取了 HTML 代码之后,我们开始分析怎样获取某一页的所有段子。 首先我们审查元素看一下,按浏览器的 F12,截图如下 20150802154147 我们可以看到,每一个段子都是

    ...
    包裹的内容。 现在我们想获取发布人,发布日期,段子内容,以及点赞的个数。不过另外注意的是,段子有些是带图片的,如果我们想在控制台显示图片是不现实的,所以我们直接把带有图片的段子给它剔除掉,只保存仅含文本的段子。 所以我们加入如下正则表达式来匹配一下,用到的方法是 re.findall 是找寻所有匹配的内容。方法的用法详情可以看前面说的正则表达式的介绍。 好,我们的正则表达式匹配语句书写如下,在原来的基础上追加如下代码

    1
    2
    3
    4
    5
    6
    content = response.read().decode('utf-8')
    pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
    'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
    items = re.findall(pattern,content)
    for item in items:
    print item[0],item[1],item[2],item[3],item[4]

    现在正则表达式在这里稍作说明 1).? 是一个固定的搭配,.和代表可以匹配任意无限多个字符,加上?表示使用非贪婪模式进行匹配,也就是我们会尽可能短地做匹配,以后我们还会大量用到 .? 的搭配。 2)(.?)代表一个分组,在这个正则表达式中我们匹配了五个分组,在后面的遍历 item 中,item[0]就代表第一个(.?)所指代的内容,item[1]就代表第二个(.?)所指代的内容,以此类推。 3)re.S 标志代表在匹配时为点任意匹配模式,点 . 也可以代表换行符。 这样我们就获取了发布人,发布时间,发布内容,附加图片以及点赞数。 在这里注意一下,我们要获取的内容如果是带有图片,直接输出出来比较繁琐,所以这里我们只获取不带图片的段子就好了。 所以,在这里我们就需要对带图片的段子进行过滤。 我们可以发现,带有图片的段子会带有类似下面的代码,而不带图片的则没有,所以,我们的正则表达式的 item[3]就是获取了下面的内容,如果不带图片,item[3]获取的内容便是空。

    1
    2
    3
    4
    5
    6
    7
    <div class="thumb">

    <a href="/article/112061287?list=hot&amp;s=4794990" target="_blank">
    <img src="http://pic.qiushibaike.com/system/pictures/11206/112061287/medium/app112061287.jpg" alt="但他们依然乐观">
    </a>

    </div>

    所以我们只需要判断 item[3]中是否含有 img 标签就可以了。 好,我们再把上述代码中的 for 循环改为下面的样子

    1
    2
    3
    4
    for item in items:
    haveImg = re.search("img",item[3])
    if not haveImg:
    print item[0],item[1],item[2],item[4]

    现在,整体的代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    # -*- coding:utf-8 -*-
    import urllib
    import urllib2
    import re

    page = 1
    url = 'http://www.qiushibaike.com/hot/page/' + str(page)
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    headers = { 'User-Agent' : user_agent }
    try:
    request = urllib2.Request(url,headers = headers)
    response = urllib2.urlopen(request)
    content = response.read().decode('utf-8')
    pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
    'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
    items = re.findall(pattern,content)
    for item in items:
    haveImg = re.search("img",item[3])
    if not haveImg:
    print item[0],item[1],item[2],item[4]
    except urllib2.URLError, e:
    if hasattr(e,"code"):
    print e.code
    if hasattr(e,"reason"):
    print e.reason

    运行一下看下效果 20150802154832 恩,带有图片的段子已经被剔除啦。是不是很开森?

    3.完善交互,设计面向对象模式

    好啦,现在最核心的部分我们已经完成啦,剩下的就是修一下边边角角的东西,我们想达到的目的是: 按下回车,读取一个段子,显示出段子的发布人,发布日期,内容以及点赞个数。 另外我们需要设计面向对象模式,引入类和方法,将代码做一下优化和封装,最后,我们的代码如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    __author__ = 'CQC'
    # -*- coding:utf-8 -*-
    import urllib
    import urllib2
    import re
    import thread
    import time

    #糗事百科爬虫类
    class QSBK:

    #初始化方法,定义一些变量
    def __init__(self):
    self.pageIndex = 1
    self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    #初始化headers
    self.headers = { 'User-Agent' : self.user_agent }
    #存放段子的变量,每一个元素是每一页的段子们
    self.stories = []
    #存放程序是否继续运行的变量
    self.enable = False
    #传入某一页的索引获得页面代码
    def getPage(self,pageIndex):
    try:
    url = 'http://www.qiushibaike.com/hot/page/' + str(pageIndex)
    #构建请求的request
    request = urllib2.Request(url,headers = self.headers)
    #利用urlopen获取页面代码
    response = urllib2.urlopen(request)
    #将页面转化为UTF-8编码
    pageCode = response.read().decode('utf-8')
    return pageCode

    except urllib2.URLError, e:
    if hasattr(e,"reason"):
    print u"连接糗事百科失败,错误原因",e.reason
    return None


    #传入某一页代码,返回本页不带图片的段子列表
    def getPageItems(self,pageIndex):
    pageCode = self.getPage(pageIndex)
    if not pageCode:
    print "页面加载失败...."
    return None
    pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
    'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
    items = re.findall(pattern,pageCode)
    #用来存储每页的段子们
    pageStories = []
    #遍历正则表达式匹配的信息
    for item in items:
    #是否含有图片
    haveImg = re.search("img",item[3])
    #如果不含有图片,把它加入list中
    if not haveImg:
    replaceBR = re.compile('<br/>')
    text = re.sub(replaceBR,"\n",item[1])
    #item[0]是一个段子的发布者,item[1]是内容,item[2]是发布时间,item[4]是点赞数
    pageStories.append([item[0].strip(),text.strip(),item[2].strip(),item[4].strip()])
    return pageStories

    #加载并提取页面的内容,加入到列表中
    def loadPage(self):
    #如果当前未看的页数少于2页,则加载新一页
    if self.enable == True:
    if len(self.stories) < 2:
    #获取新一页
    pageStories = self.getPageItems(self.pageIndex)
    #将该页的段子存放到全局list中
    if pageStories:
    self.stories.append(pageStories)
    #获取完之后页码索引加一,表示下次读取下一页
    self.pageIndex += 1

    #调用该方法,每次敲回车打印输出一个段子
    def getOneStory(self,pageStories,page):
    #遍历一页的段子
    for story in pageStories:
    #等待用户输入
    input = raw_input()
    #每当输入回车一次,判断一下是否要加载新页面
    self.loadPage()
    #如果输入Q则程序结束
    if input == "Q":
    self.enable = False
    return
    print u"第%d页\t发布人:%s\t发布时间:%s\t赞:%s\n%s" %(page,story[0],story[2],story[3],story[1])

    #开始方法
    def start(self):
    print u"正在读取糗事百科,按回车查看新段子,Q退出"
    #使变量为True,程序可以正常运行
    self.enable = True
    #先加载一页内容
    self.loadPage()
    #局部变量,控制当前读到了第几页
    nowPage = 0
    while self.enable:
    if len(self.stories)>0:
    #从全局list中获取一页的段子
    pageStories = self.stories[0]
    #当前读到的页数加一
    nowPage += 1
    #将全局list中第一个元素删除,因为已经取出
    del self.stories[0]
    #输出该页的段子
    self.getOneStory(pageStories,nowPage)


    spider = QSBK()
    spider.start()

    好啦,大家来测试一下吧,点一下回车会输出一个段子,包括发布人,发布时间,段子内容以及点赞数,是不是感觉爽爆了! 我们第一个爬虫实战项目介绍到这里,欢迎大家继续关注,小伙伴们加油!

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    如下为原文。

    初级的爬虫我们利用 urllib 和 urllib2 库以及正则表达式就可以完成了,不过还有更加强大的工具,爬虫框架 Scrapy,这安装过程也是煞费苦心哪,在此整理如下。

    Windows 平台:

    我的系统是 Win7,首先,你要有 Python,我用的是 2.7.7 版本,Python3 相仿,只是一些源文件不同。 官网文档:http://doc.scrapy.org/en/latest/intro/install.html,最权威哒,下面是我的亲身体验过程。 1.安装 Python 安装过程我就不多说啦,我的电脑中已经安装了 Python 2.7.7 版本啦,安装完之后记得配置环境变量,比如我的安装在 D 盘,D:\python2.7.7,就把以下两个路径添加到 Path 变量中

    1
    D:\python2.7.7;D:\python2.7.7\Scripts

    配置好了之后,在命令行中输入 python —version,如果没有提示错误,则安装成功 QQ截图20150211171953 2.安装 pywin32 在 windows 下,必须安装 pywin32,安装地址:http://sourceforge.net/projects/pywin32/ 下载对应版本的 pywin32,直接双击安装即可,安装完毕之后验证: QQ截图20150211171713 在 python 命令行下输入 import win32com 如果没有提示错误,则证明安装成功 3.安装 pip pip 是用来安装其他必要包的工具,首先下载 get-pip.py 下载好之后,选中该文件所在路径,执行下面的命令

    1
    python get-pip.py

    执行命令后便会安装好 pip,并且同时,它帮你安装了setuptools 安装完了之后在命令行中执行

    1
    pip --version

    如果提示如下,说明就安装成功了,如果提示不是内部或外部命令,那么就检查一下环境变量有没有配置好吧,有两个路径。 QQ截图20150211171001 4.安装 pyOPENSSL 在 Windows 下,是没有预装 pyOPENSSL 的,而在 Linux 下是已经安装好的。 安装地址:https://launchpad.net/pyopenssl 5.安装 lxml lxml 的详细介绍 点我 ,是一种使用 Python 编写的库,可以迅速、灵活地处理 XML 直接执行如下命令

    1
    pip install lxml

    就可完成安装,如果提示 Microsoft Visual C++库没安装,则 点我 下载支持的库。 6.安装 Scrapy 最后就是激动人心的时刻啦,上面的铺垫做好了,我们终于可以享受到胜利的果实啦! 执行如下命令

    1
    pip install Scrapy

    QQ截图20150211172637 pip 会另外下载其他依赖的包,这些就不要我们手动安装啦,等待一会,大功告成! 7.验证安装 输入 Scrapy 如果提示如下命令,就证明安装成功啦,如果失败了,请检查上述步骤有何疏漏。 QQ截图20150211172456

    Linux Ubuntu 平台:

    Linux 下安装非常简单,只需要执行几条命令几个 1.安装 Python

    1
    sudo apt-get install python2.7 python2.7-dev

    2.安装 pip 首先下载 get-pip.py 下载好之后,选中该文件所在路径,执行下面的命令

    1
    sudo python get-pip.py

    3.直接安装 Scrapy 由于 Linux 下已经预装了 lxml 和 OPENSSL 如果想验证 lxml ,可以分别输入

    1
    sudo pip install lxml

    出现下面的提示这证明已经安装成功

    1
    Requirement already satisfied (use --upgrade to upgrade): lxml in /usr/lib/python2.7/dist-packages

    如果想验证 openssl,则直接输入 openssl 即可,如果跳转到 OPENSSL 命令行,则安装成功。 接下来直接安装 Scrapy 即可

    1
    sudo pip install Scrapy

    安装完毕之后,输入 scrapy 注意,这里 linux 下不要输入 Scrapy,linux 依然严格区分大小写的,感谢 kamen 童鞋提醒。 如果出现如下提示,这证明安装成功

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Usage:
    scrapy <command> [options] [args]

    Available commands:
    bench Run quick benchmark test
    fetch Fetch a URL using the Scrapy downloader
    runspider Run a self-contained spider (without creating a project)
    settings Get settings values
    shell Interactive scraping console
    startproject Create new project
    version Print Scrapy version
    view Open URL in browser, as seen by Scrapy

    [ more ] More commands available when run from project directory

    截图如下 2015-02-12 01:00:22 的屏幕截图 如有问题,欢迎留言!祝各位小伙伴顺利安装!

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    原文

    在前面我们已经搞定了怎样获取页面的内容,不过还差一步,这么多杂乱的代码夹杂文字我们怎样把它提取出来整理呢?下面就开始介绍一个十分强大的工具,正则表达式!

    1.了解正则表达式

    正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

    正则表达式是用来匹配字符串非常强大的工具,在其他编程语言中同样有正则表达式的概念,Python 同样不例外,利用了正则表达式,我们想要从返回的页面内容提取出我们想要的内容就易如反掌了。

    正则表达式的大致匹配过程是: 1.依次拿出表达式和文本中的字符比较, 2.如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。 3.如果表达式中有量词或边界,这个过程会稍微有一些不同。

    2.正则表达式的语法规则

    下面是 Python 中正则表达式的一些匹配规则,图片资料来自 CSDN 20130515113723855

    3.正则表达式相关注解

    (1)数量词的贪婪模式与非贪婪模式

    正则表达式通常用于在文本中查找匹配的字符串。Python 里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪的则相反,总是尝试匹配尽可能少的字符。例如:正则表达式”ab“如果用于查找”abbbc”,将找到”abbb”。而如果使用非贪婪的数量词”ab?”,将找到”a”。 注:我们一般使用非贪婪模式来提取。

    (2)反斜杠问题

    与大多数编程语言相同,正则表达式里使用”\“作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符”\“,那么使用编程语言表示的正则表达式里将需要 4 个反斜杠”\\\\“:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。 Python 里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用 r”\\“表示。同样,匹配一个数字的”\\d”可以写成 r”\d”。有了原生字符串,妈妈也不用担心是不是漏写了反斜杠,写出来的表达式也更直观勒。

    4.Python Re 模块

    Python 自带了 re 模块,它提供了对正则表达式的支持。主要用到的方法列举如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #返回pattern对象
    re.compile(string[,flag])
    #以下为匹配所用函数
    re.match(pattern, string[, flags])
    re.search(pattern, string[, flags])
    re.split(pattern, string[, maxsplit])
    re.findall(pattern, string[, flags])
    re.finditer(pattern, string[, flags])
    re.sub(pattern, repl, string[, count])
    re.subn(pattern, repl, string[, count])

    在介绍这几个方法之前,我们先来介绍一下 pattern 的概念,pattern 可以理解为一个匹配模式,那么我们怎么获得这个匹配模式呢?很简单,我们需要利用 re.compile 方法就可以。例如

    1
    pattern = re.compile(r'hello')

    在参数中我们传入了原生字符串对象,通过 compile 方法编译生成一个 pattern 对象,然后我们利用这个对象来进行进一步的匹配。 另外大家可能注意到了另一个参数 flags,在这里解释一下这个参数的含义: 参数 flag 是匹配模式,取值可以使用按位或运算符’|’表示同时生效,比如 re.I | re.M。 可选值有:

    1
    2
    3
    4
    5
    6
    • re.I(全拼:IGNORECASE): 忽略大小写(括号内是完整写法,下同)
    • re.M(全拼:MULTILINE): 多行模式,改变'^''$'的行为(参见上图)
    • re.S(全拼:DOTALL): 点任意匹配模式,改变'.'的行为
    • re.L(全拼:LOCALE): 使预定字符类 \w \W \b \B \s \S 取决于当前区域设定
    • re.U(全拼:UNICODE): 使预定字符类 \w \W \b \B \s \S \d \D 取决于unicode定义的字符属性
    • re.X(全拼:VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。

    在刚才所说的另外几个方法例如 re.match 里我们就需要用到这个 pattern 了,下面我们一一介绍。

    注:以下七个方法中的 flags 同样是代表匹配模式的意思,如果在 pattern 生成时已经指明了 flags,那么在下面的方法中就不需要传入这个参数了。

    (1)re.match(pattern, string[, flags])

    这个方法将会从 string(我们要匹配的字符串)的开头开始,尝试匹配 pattern,一直向后匹配,如果遇到无法匹配的字符,立即返回 None,如果匹配未结束已经到达 string 的末尾,也会返回 None。两个结果均表示匹配失败,否则匹配 pattern 成功,同时匹配终止,不再对 string 向后匹配。下面我们通过一个例子理解一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    __author__ = 'CQC'
    # -*- coding: utf-8 -*-

    #导入re模块
    import re

    # 将正则表达式编译成Pattern对象,注意hello前面的r的意思是“原生字符串”
    pattern = re.compile(r'hello')

    # 使用re.match匹配文本,获得匹配结果,无法匹配时将返回None
    result1 = re.match(pattern,'hello')
    result2 = re.match(pattern,'helloo CQC!')
    result3 = re.match(pattern,'helo CQC!')
    result4 = re.match(pattern,'hello CQC!')

    #如果1匹配成功
    if result1:
    # 使用Match获得分组信息
    print result1.group()
    else:
    print '1匹配失败!'


    #如果2匹配成功
    if result2:
    # 使用Match获得分组信息
    print result2.group()
    else:
    print '2匹配失败!'


    #如果3匹配成功
    if result3:
    # 使用Match获得分组信息
    print result3.group()
    else:
    print '3匹配失败!'

    #如果4匹配成功
    if result4:
    # 使用Match获得分组信息
    print result4.group()
    else:
    print '4匹配失败!'

    运行结果

    1
    2
    3
    4
    hello
    hello
    3匹配失败!
    hello

    匹配分析 1.第一个匹配,pattern 正则表达式为’hello’,我们匹配的目标字符串 string 也为 hello,从头至尾完全匹配,匹配成功。 2.第二个匹配,string 为 helloo CQC,从 string 头开始匹配 pattern 完全可以匹配,pattern 匹配结束,同时匹配终止,后面的 o CQC 不再匹配,返回匹配成功的信息。 3.第三个匹配,string 为 helo CQC,从 string 头开始匹配 pattern,发现到 ‘o’ 时无法完成匹配,匹配终止,返回 None 4.第四个匹配,同第二个匹配原理,即使遇到了空格符也不会受影响。 我们还看到最后打印出了 result.group(),这个是什么意思呢?下面我们说一下关于 match 对象的的属性和方法 Match 对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用 Match 提供的可读属性或方法来获取这些信息。

    属性: 1.string: 匹配时使用的文本。 2.re: 匹配时使用的 Pattern 对象。 3.pos: 文本中正则表达式开始搜索的索引。值与 Pattern.match()和 Pattern.seach()方法的同名参数相同。 4.endpos: 文本中正则表达式结束搜索的索引。值与 Pattern.match()和 Pattern.seach()方法的同名参数相同。 5.lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为 None。 6.lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为 None。 方法: 1.group([group1, …]): 获得一个或多个分组截获的字符串;指定多个参数时将以元组形式返回。group1 可以使用编号也可以使用别名;编号 0 代表整个匹配的子串;不填写参数时,返回 group(0);没有截获字符串的组返回 None;截获了多次的组返回最后一次截获的子串。 2.groups([default]): 以元组形式返回全部分组截获的字符串。相当于调用 group(1,2,…last)。default 表示没有截获字符串的组以这个值替代,默认为 None。 3.groupdict([default]): 返回以有别名的组的别名为键、以该组截获的子串为值的字典,没有别名的组不包含在内。default 含义同上。 4.start([group]): 返回指定的组截获的子串在 string 中的起始索引(子串第一个字符的索引)。group 默认值为 0。 5.end([group]): 返回指定的组截获的子串在 string 中的结束索引(子串最后一个字符的索引+1)。group 默认值为 0。 6.span([group]): 返回(start(group), end(group))。 7.expand(template): 将匹配到的分组代入 template 中然后返回。template 中可以使用\id 或\g、\g 引用分组,但不能使用编号 0。\id 与\g 是等价的;但\10 将被认为是第 10 个分组,如果你想表达\1 之后是字符’0’,只能使用\g0。

    下面我们用一个例子来体会一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    # -*- coding: utf-8 -*-
    #一个简单的match实例

    import re
    # 匹配如下内容:单词+空格+单词+任意字符
    m = re.match(r'(\w+) (\w+)(?P<sign>.*)', 'hello world!')

    print "m.string:", m.string
    print "m.re:", m.re
    print "m.pos:", m.pos
    print "m.endpos:", m.endpos
    print "m.lastindex:", m.lastindex
    print "m.lastgroup:", m.lastgroup
    print "m.group():", m.group()
    print "m.group(1,2):", m.group(1, 2)
    print "m.groups():", m.groups()
    print "m.groupdict():", m.groupdict()
    print "m.start(2):", m.start(2)
    print "m.end(2):", m.end(2)
    print "m.span(2):", m.span(2)
    print r"m.expand(r'\g \g\g'):", m.expand(r'\2 \1\3')

    ### output ###
    # m.string: hello world!
    # m.re:
    # m.pos: 0
    # m.endpos: 12
    # m.lastindex: 3
    # m.lastgroup: sign
    # m.group(1,2): ('hello', 'world')
    # m.groups(): ('hello', 'world', '!')
    # m.groupdict(): {'sign': '!'}
    # m.start(2): 6
    # m.end(2): 11
    # m.span(2): (6, 11)
    # m.expand(r'\2 \1\3'): world hello!

    (2)re.search(pattern, string[, flags])

    search 方法与 match 方法极其类似,区别在于 match()函数只检测 re 是不是在 string 的开始位置匹配,search()会扫描整个 string 查找匹配,match()只有在 0 位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回 None。同样,search 方法的返回对象同样 match()返回对象的方法和属性。我们用一个例子感受一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #导入re模块
    import re

    # 将正则表达式编译成Pattern对象
    pattern = re.compile(r'world')
    # 使用search()查找匹配的子串,不存在能匹配的子串时将返回None
    # 这个例子中使用match()无法成功匹配
    match = re.search(pattern,'hello world!')
    if match:
    # 使用Match获得分组信息
    print match.group()
    ### 输出 ###
    # world

    (3)re.split(pattern, string[, maxsplit])

    按照能够匹配的子串将 string 分割后返回列表。maxsplit 用于指定最大分割次数,不指定将全部分割。我们通过下面的例子感受一下。

    1
    2
    3
    4
    5
    6
    7
    import re

    pattern = re.compile(r'\d+')
    print re.split(pattern,'one1two2three3four4')

    ### 输出 ###
    # ['one', 'two', 'three', 'four', '']

    (4)re.findall(pattern, string[, flags])

    搜索 string,以列表形式返回全部能匹配的子串。我们通过这个例子来感受一下

    1
    2
    3
    4
    5
    6
    7
    import re

    pattern = re.compile(r'\d+')
    print re.findall(pattern,'one1two2three3four4')

    ### 输出 ###
    # ['1', '2', '3', '4']

    (5)re.finditer(pattern, string[, flags])

    搜索 string,返回一个顺序访问每一个匹配结果(Match 对象)的迭代器。我们通过下面的例子来感受一下

    1
    2
    3
    4
    5
    6
    7
    8
    import re

    pattern = re.compile(r'\d+')
    for m in re.finditer(pattern,'one1two2three3four4'):
    print m.group(),

    ### 输出 ###
    # 1 2 3 4

    (6)re.sub(pattern, repl, string[, count])

    使用 repl 替换 string 中每一个匹配的子串后返回替换后的字符串。 当 repl 是一个字符串时,可以使用\id 或\g、\g 引用分组,但不能使用编号 0。 当 repl 是一个方法时,这个方法应当只接受一个参数(Match 对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。 count 用于指定最多替换次数,不指定时全部替换。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import re

    pattern = re.compile(r'(\w+) (\w+)')
    s = 'i say, hello world!'

    print re.sub(pattern,r'\2 \1', s)

    def func(m):
    return m.group(1).title() + ' ' + m.group(2).title()

    print re.sub(pattern,func, s)

    ### output ###
    # say i, world hello!
    # I Say, Hello World!

    (7)re.subn(pattern, repl, string[, count])

    返回 (sub(repl, string[, count]), 替换次数)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import re

    pattern = re.compile(r'(\w+) (\w+)')
    s = 'i say, hello world!'

    print re.subn(pattern,r'\2 \1', s)

    def func(m):
    return m.group(1).title() + ' ' + m.group(2).title()

    print re.subn(pattern,func, s)

    ### output ###
    # ('say i, world hello!', 2)
    # ('I Say, Hello World!', 2)

    5.Python Re 模块的另一种使用方式

    在上面我们介绍了 7 个工具方法,例如 match,search 等等,不过调用方式都是 re.match,re.search 的方式,其实还有另外一种调用方式,可以通过 pattern.match,pattern.search 调用,这样调用便不用将 pattern 作为第一个参数传入了,大家想怎样调用皆可。 函数 API 列表

    1
    2
    3
    4
    5
    6
    7
    match(string[, pos[, endpos]]) | re.match(pattern, string[, flags])
    search(string[, pos[, endpos]]) | re.search(pattern, string[, flags])
    split(string[, maxsplit]) | re.split(pattern, string[, maxsplit])
    findall(string[, pos[, endpos]]) | re.findall(pattern, string[, flags])
    finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags])
    sub(repl, string[, count]) | re.sub(pattern, repl, string[, count])
    subn(repl, string[, count]) |re.sub(pattern, repl, string[, count])

    具体的调用方法不必详说了,原理都类似,只是参数的变化不同。小伙伴们尝试一下吧~ 小伙伴们加油,即使这一节看得云里雾里的也没关系,接下来我们会通过一些实战例子来帮助大家熟练掌握正则表达式的。 参考文章:此文章部分内容出自 CNBlogs

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    如下为原文。

    大家好哈,上一节我们研究了一下爬虫的异常处理问题,那么接下来我们一起来看一下 Cookie 的使用。 为什么要使用 Cookie 呢? Cookie,指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密) 比如说有些网站需要登录后才能访问某个页面,在登录之前,你想抓取某个页面内容是不允许的。那么我们可以利用 Urllib2 库保存我们登录的 Cookie,然后再抓取其他页面就达到目的了。 在此之前呢,我们必须先介绍一个 opener 的概念。

    1.Opener

    当你获取一个 URL 你使用一个 opener(一个 urllib2.OpenerDirector 的实例)。在前面,我们都是使用的默认的 opener,也就是 urlopen。它是一个特殊的 opener,可以理解成 opener 的一个特殊实例,传入的参数仅仅是 url,data,timeout。 如果我们需要用到 Cookie,只用这个 opener 是不能达到目的的,所以我们需要创建更一般的 opener 来实现对 Cookie 的设置。

    2.Cookielib

    cookielib 模块的主要作用是提供可存储 cookie 的对象,以便于与 urllib2 模块配合使用来访问 Internet 资源。Cookielib 模块非常强大,我们可以利用本模块的 CookieJar 类的对象来捕获 cookie 并在后续连接请求时重新发送,比如可以实现模拟登录功能。该模块主要的对象有 CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar。 它们的关系:CookieJar ——派生——>FileCookieJar ——派生——->MozillaCookieJar 和 LWPCookieJar

    首先,我们先利用 CookieJar 对象实现获取 cookie 的功能,存储到变量中,先来感受一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import urllib2
    import cookielib
    #声明一个CookieJar对象实例来保存cookie
    cookie = cookielib.CookieJar()
    #利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
    handler=urllib2.HTTPCookieProcessor(cookie)
    #通过handler来构建opener
    opener = urllib2.build_opener(handler)
    #此处的open方法同urllib2的urlopen方法,也可以传入request
    response = opener.open('http://www.baidu.com')
    for item in cookie:
    print 'Name = '+item.name
    print 'Value = '+item.value

    我们使用以上方法将 cookie 保存到变量中,然后打印出了 cookie 中的值,运行结果如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Name = BAIDUID
    Value = B07B663B645729F11F659C02AAE65B4C:FG=1
    Name = BAIDUPSID
    Value = B07B663B645729F11F659C02AAE65B4C
    Name = H_PS_PSSID
    Value = 12527_11076_1438_10633
    Name = BDSVRTM
    Value = 0
    Name = BD_HOME
    Value = 0

    在上面的方法中,我们将 cookie 保存到了 cookie 这个变量中,如果我们想将 cookie 保存到文件中该怎么做呢?这时,我们就要用到 FileCookieJar 这个对象了,在这里我们使用它的子类 MozillaCookieJar 来实现 Cookie 的保存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import cookielib
    import urllib2

    #设置保存cookie的文件,同级目录下的cookie.txt
    filename = 'cookie.txt'
    #声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
    cookie = cookielib.MozillaCookieJar(filename)
    #利用urllib2库的HTTPCookieProcessor对象来创建cookie处理器
    handler = urllib2.HTTPCookieProcessor(cookie)
    #通过handler来构建opener
    opener = urllib2.build_opener(handler)
    #创建一个请求,原理同urllib2的urlopen
    response = opener.open("http://www.baidu.com")
    #保存cookie到文件
    cookie.save(ignore_discard=True, ignore_expires=True)

    关于最后 save 方法的两个参数在此说明一下: 官方解释如下:

    ignore_discard: save even cookies set to be discarded. ignore_expires: save even cookies that have expiredThe file is overwritten if it already exists

    由此可见,ignore_discard 的意思是即使 cookies 将被丢弃也将它保存下来,ignore_expires 的意思是如果在该文件中 cookies 已经存在,则覆盖原文件写入,在这里,我们将这两个全部设置为 True。运行之后,cookies 将被保存到 cookie.txt 文件中,我们查看一下内容,附图如下 QQ截图20150215215136

    那么我们已经做到把 Cookie 保存到文件中了,如果以后想使用,可以利用下面的方法来读取 cookie 并访问网站,感受一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import cookielib
    import urllib2

    #创建MozillaCookieJar实例对象
    cookie = cookielib.MozillaCookieJar()
    #从文件中读取cookie内容到变量
    cookie.load('cookie.txt', ignore_discard=True, ignore_expires=True)
    #创建请求的request
    req = urllib2.Request("http://www.baidu.com")
    #利用urllib2的build_opener方法创建一个opener
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
    response = opener.open(req)
    print response.read()

    设想,如果我们的 cookie.txt 文件中保存的是某个人登录百度的 cookie,那么我们提取出这个 cookie 文件内容,就可以用以上方法模拟这个人的账号登录百度。

    下面我们以我们学校的教育系统为例,利用 cookie 实现模拟登录,并将 cookie 信息保存到文本文件中,来感受一下 cookie 大法吧! 注意:密码我改了啊,别偷偷登录本宫的选课系统 o(╯□╰)o

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import urllib
    import urllib2
    import cookielib

    filename = 'cookie.txt'
    #声明一个MozillaCookieJar对象实例来保存cookie,之后写入文件
    cookie = cookielib.MozillaCookieJar(filename)
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
    postdata = urllib.urlencode({
    'stuid':'201200131012',
    'pwd':'23342321'
    })
    #登录教务系统的URL
    loginUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bks_login2.login'
    #模拟登录,并把cookie保存到变量
    result = opener.open(loginUrl,postdata)
    #保存cookie到cookie.txt中
    cookie.save(ignore_discard=True, ignore_expires=True)
    #利用cookie请求访问另一个网址,此网址是成绩查询网址
    gradeUrl = 'http://jwxt.sdu.edu.cn:7890/pls/wwwbks/bkscjcx.curscopre'
    #请求访问成绩查询网址
    result = opener.open(gradeUrl)
    print result.read()

    以上程序的原理如下 创建一个带有 cookie 的 opener,在访问登录的 URL 时,将登录后的 cookie 保存下来,然后利用这个 cookie 来访问其他网址。 如登录之后才能查看的成绩查询呀,本学期课表呀等等网址,模拟登录就这么实现啦,是不是很酷炫? 好,小伙伴们要加油哦!我们现在可以顺利获取网站信息了,接下来就是把网站里面有效内容提取出来,下一节我们去会会正则表达式!

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    如下为原文。

    大家好,本节在这里主要说的是URLError还有HTTPError,以及对它们的一些处理。

    1.URLError

    首先解释下URLError可能产生的原因:

    • 网络无连接,即本机无法上网
    • 连接不到特定的服务器
    • 服务器不存在

    在代码中,我们需要用try-except语句来包围并捕获相应的异常。下面是一个例子,先感受下它的风骚

    1
    2
    3
    4
    5
    6
    7
    import urllib2

    requset = urllib2.Request('http://www.xxxxx.com')
    try:
    urllib2.urlopen(request)
    except urllib2.URLError, e:
    print e.reason

    我们利用了 urlopen方法访问了一个不存在的网址,运行结果如下:

    1
    [Errno 11004] getaddrinfo failed

    它说明了错误代号是11004,错误原因是 getaddrinfo failed 2.HTTPError HTTPError是URLError的子类,在你利用urlopen方法发出一个请求时,服务器上都会对应一个应答对象response,其中它包含一个数字”状态码”。举个例子,假如response是一个”重定向”,需定位到别的地址获取文档,urllib2将对此进行处理。 其他不能处理的,urlopen会产生一个HTTPError,对应相应的状态吗,HTTP状态码表示HTTP协议所返回的响应的状态。下面将状态码归结如下:

    100:继续 客户端应当继续发送请求。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。 101: 转换协议 在发送完这个响应最后的空行后,服务器将会切换到在Upgrade 消息头中定义的那些协议。只有在切换新的协议更有好处的时候才应该采取类似措施。 102:继续处理 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。 200:请求成功 处理方式:获得响应的内容,进行处理 201:请求完成,结果是创建了新资源。新创建资源的URI可在响应的实体中得到 处理方式:爬虫中不会遇到 202:请求被接受,但处理尚未完成 处理方式:阻塞等待 204:服务器端已经实现了请求,但是没有返回新的信 息。如果客户是用户代理,则无须为此更新自身的文档视图。 处理方式:丢弃 300:该状态码不被HTTP/1.0的应用程序直接使用, 只是作为3XX类型回应的默认解释。存在多个可用的被请求资源。 处理方式:若程序中能够处理,则进行进一步处理,如果程序中不能处理,则丢弃 301:请求到的资源都会分配一个永久的URL,这样就可以在将来通过该URL来访问此资源 处理方式:重定向到分配的URL 302:请求到的资源在一个不同的URL处临时保存 处理方式:重定向到临时的URL 304:请求的资源未更新 处理方式:丢弃 400:非法请求 处理方式:丢弃 401:未授权 处理方式:丢弃 403:禁止 处理方式:丢弃 404:没有找到 处理方式:丢弃 500:服务器内部错误 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。 501:服务器无法识别 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。 502:错误网关 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。 503:服务出错 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。

    HTTPError实例产生后会有一个code属性,这就是是服务器发送的相关错误号。 因为urllib2可以为你处理重定向,也就是3开头的代号可以被处理,并且100-299范围的号码指示成功,所以你只能看到400-599的错误号码。 下面我们写一个例子来感受一下,捕获的异常是HTTPError,它会带有一个code属性,就是错误代号,另外我们又打印了reason属性,这是它的父类URLError的属性。

    1
    2
    3
    4
    5
    6
    7
    8
    import urllib2

    req = urllib2.Request('http://blog.csdn.net/cqcre')
    try:
    urllib2.urlopen(req)
    except urllib2.HTTPError, e:
    print e.code
    print e.reason

    运行结果如下

    1
    2
    403
    Forbidden

    错误代号是403,错误原因是Forbidden,说明服务器禁止访问。 我们知道,HTTPError的父类是URLError,根据编程经验,父类的异常应当写到子类异常的后面,如果子类捕获不到,那么可以捕获父类的异常,所以上述的代码可以这么改写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import urllib2

    req = urllib2.Request('http://blog.csdn.net/cqcre')
    try:
    urllib2.urlopen(req)
    except urllib2.HTTPError, e:
    print e.code
    except urllib2.URLError, e:
    print e.reason
    else:
    print "OK"

    如果捕获到了HTTPError,则输出code,不会再处理URLError异常。如果发生的不是HTTPError,则会去捕获URLError异常,输出错误原因。 另外还可以加入 hasattr属性提前对属性进行判断,代码改写如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import urllib2

    req = urllib2.Request('http://blog.csdn.net/cqcre')
    try:
    urllib2.urlopen(req)
    except urllib2.URLError, e:
    if hasattr(e,"reason"):
    print e.reason
    else:
    print "OK"

    首先对异常的属性进行判断,以免出现属性输出报错的现象。 以上,就是对URLError和HTTPError的相关介绍,以及相应的错误处理办法,小伙伴们加油!

    Python

    2022 年最新 Python3 网络爬虫教程

    大家好,我是崔庆才,由于爬虫技术不断迭代升级,一些旧的教程已经过时、案例已经过期,最前沿的爬虫技术比如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等技术层出不穷,我最近新出了一套最新最全面的 Python3 网络爬虫系列教程。

    博主自荐:截止 2022 年,可以将最前沿最全面的爬虫技术都涵盖的教程,如异步、JavaScript 逆向、安卓逆向、智能解析、WebAssembly、大规模分布式、Kubernetes 等,市面上目前就这一套了。

    最新教程对旧的爬虫技术内容进行了全面更新,搭建了全新的案例平台进行全面讲解,保证案例稳定有效不过期。

    教程请移步:

    【2022 版】Python3 网络爬虫学习教程

    如下为原文。

    1.设置 Headers

    有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些 Headers 的属性。 首先,打开我们的浏览器,调试浏览器 F12,我用的是 Chrome,打开网络监听,示意如下,比如知乎,点登录之后,我们会发现登陆之后界面都变化了,出现一个新的界面,实质上这个页面包含了许许多多的内容,这些内容也不是一次性就加载完成的,实质上是执行了好多次请求,一般是首先请求 HTML 文件,然后加载 JS,CSS 等等,经过多次请求之后,网页的骨架和肌肉全了,整个网页的效果也就出来了。 2015-02-13 01:31:55 的屏幕截图 拆分这些请求,我们只看一第一个请求,你可以看到,有个 Request URL,还有 headers,下面便是 response,图片显示得不全,小伙伴们可以亲身实验一下。那么这个头中包含了许许多多是信息,有文件编码啦,压缩方式啦,请求的 agent 啦等等。 其中,agent 就是请求的身份,如果没有写入请求身份,那么服务器不一定会响应,所以可以在 headers 中设置 agent,例如下面的例子,这个例子只是说明了怎样设置的 headers,小伙伴们看一下设置格式就好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import urllib
    import urllib2

    url = 'http://www.server.com/login'
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
    values = {'username' : 'cqc', 'password' : 'XXXX' }
    headers = { 'User-Agent' : user_agent }
    data = urllib.urlencode(values)
    request = urllib2.Request(url, data, headers)
    response = urllib2.urlopen(request)
    page = response.read()

    这样,我们设置了一个 headers,在构建 request 时传入,在请求时,就加入了 headers 传送,服务器若识别了是浏览器发来的请求,就会得到响应。 另外,我们还有对付”反盗链”的方式,对付防盗链,服务器会识别 headers 中的 referer 是不是它自己,如果不是,有的服务器不会响应,所以我们还可以在 headers 中加入 referer 例如我们可以构建下面的 headers

    1
    2
    headers = { 'User-Agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'  ,
    'Referer':'http://www.zhihu.com/articles' }

    同上面的方法,在传送请求时把 headers 传入 Request 参数里,这样就能应付防盗链了。 另外 headers 的一些属性,下面的需要特别注意一下:

    User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求 Content-Type : 在使用 REST 接口时,服务器会检查该值,用来确定 HTTP Body 中的内容该怎样解析。 application/xml : 在 XML RPC,如 RESTful/SOAP 调用时使用 application/json : 在 JSON RPC 调用时使用 application/x-www-form-urlencoded : 浏览器提交 Web 表单时使用 在使用服务器提供的 RESTful 或 SOAP 服务时, Content-Type 设置错误会导致服务器拒绝服务

    其他的有必要的可以审查浏览器的 headers 内容,在构建时写入同样的数据即可。

    2. Proxy(代理)的设置

    urllib2 默认会使用环境变量 http_proxy 来设置 HTTP Proxy。假如一个网站它会检测某一段时间某个 IP 的访问次数,如果访问次数过多,它会禁止你的访问。所以你可以设置一些代理服务器来帮助你做工作,每隔一段时间换一个代理,网站君都不知道是谁在捣鬼了,这酸爽! 下面一段代码说明了代理的设置用法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    import urllib2
    enable_proxy = True
    proxy_handler = urllib2.ProxyHandler({"http" : 'http://some-proxy.com:8080'})
    null_proxy_handler = urllib2.ProxyHandler({})
    if enable_proxy:
    opener = urllib2.build_opener(proxy_handler)
    else:
    opener = urllib2.build_opener(null_proxy_handler)
    urllib2.install_opener(opener)

    3.Timeout 设置

    上一节已经说过 urlopen 方法了,第三个参数就是 timeout 的设置,可以设置等待多久超时,为了解决一些网站实在响应过慢而造成的影响。 例如下面的代码,如果第二个参数 data 为空那么要特别指定是 timeout 是多少,写明形参,如果 data 已经传入,则不必声明。

    1
    2
    import urllib2
    response = urllib2.urlopen('http://www.baidu.com', timeout=10)
    1
    2
    import urllib2
    response = urllib2.urlopen('http://www.baidu.com',data, 10)