Skip to content

Commit

Permalink
更新了部分文档和QQ群信息
Browse files Browse the repository at this point in the history
  • Loading branch information
jackfrued committed Jul 20, 2019
1 parent 0a4d03f commit d9ec811
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 142 deletions.
33 changes: 21 additions & 12 deletions Day66-75/66.网络爬虫和相关工具.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ HTTP响应(响应行+响应头+空行+消息体):
3. HTTPie:命令行HTTP客户端。

```Shell
$ http --header http://www.scu.edu.cn
pip3 install httpie
```

```Shell
http --header http://www.scu.edu.cn
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, max-age=600
Expand All @@ -136,6 +140,10 @@ HTTP响应(响应行+响应头+空行+消息体):

4. BuiltWith:识别网站所用技术的工具。

```Shell
pip3 install builtwith
```

```Python
>>> import builtwith
>>> builtwith.parse('http://www.bootcss.com/')
Expand All @@ -149,6 +157,10 @@ HTTP响应(响应行+响应头+空行+消息体):

5. python-whois:查询网站所有者的工具。

```Shell
pip3 install python-whois
```

```Python
>>> import whois
>>> whois.whois('baidu.com')
Expand All @@ -158,13 +170,10 @@ HTTP响应(响应行+响应头+空行+消息体):
6. robotparser:解析robots.txt的工具。

```Python

>>> from urllib import robotparser
>>> parser = robotparser.RobotFileParser()
>>> parser.set_url('https://www.taobao.com/robots.txt')
>>> parser.read()
>>> parser.can_fetch('Hellokitty', 'http://www.taobao.com/article')
False
>>> parser.can_fetch('Baiduspider', 'http://www.taobao.com/article')
True
>>> parser.can_fetch('Baiduspider', 'http://www.taobao.com/product')
Expand Down Expand Up @@ -200,8 +209,8 @@ import ssl
from pymysql import Error


# 通过指定的字符集对页面进行解码(不是每个网站都将字符集设置为utf-8)
def decode_page(page_bytes, charsets=('utf-8',)):
"""通过指定的字符集对页面进行解码(不是每个网站都将字符集设置为utf-8)"""
page_html = None
for charset in charsets:
try:
Expand All @@ -213,8 +222,8 @@ def decode_page(page_bytes, charsets=('utf-8',)):
return page_html


# 获取页面的HTML代码(通过递归实现指定次数的重试操作)
def get_page_html(seed_url, *, retry_times=3, charsets=('utf-8',)):
"""获取页面的HTML代码(通过递归实现指定次数的重试操作)"""
page_html = None
try:
page_html = decode_page(urlopen(seed_url).read(), charsets)
Expand All @@ -226,14 +235,14 @@ def get_page_html(seed_url, *, retry_times=3, charsets=('utf-8',)):
return page_html


# 从页面中提取需要的部分(通常是链接也可以通过正则表达式进行指定)
def get_matched_parts(page_html, pattern_str, pattern_ignore_case=re.I):
"""从页面中提取需要的部分(通常是链接也可以通过正则表达式进行指定)"""
pattern_regex = re.compile(pattern_str, pattern_ignore_case)
return pattern_regex.findall(page_html) if page_html else []


# 开始执行爬虫程序并对指定的数据进行持久化操作
def start_crawl(seed_url, match_pattern, *, max_depth=-1):
"""开始执行爬虫程序并对指定的数据进行持久化操作"""
conn = pymysql.connect(host='localhost', port=3306,
database='crawler', user='root',
password='123456', charset='utf8')
Expand Down Expand Up @@ -268,6 +277,7 @@ def start_crawl(seed_url, match_pattern, *, max_depth=-1):


def main():
"""主函数"""
ssl._create_default_https_context = ssl._create_unverified_context
start_crawl('http://sports.sohu.com/nba_a.shtml',
r'<a[^>]+test=a\s[^>]*href=["\'](.*?)["\']',
Expand All @@ -276,18 +286,17 @@ def main():

if __name__ == '__main__':
main()

```

由于使用了MySQL实现持久化操作,所以要先启动MySQL服务器再运行该程序
由于使用了MySQL实现持久化操作,所以要先启动MySQL服务器并创建名为`crawler`的数据库和名为`tb_result`的二维表才能运行该程序

### 爬虫注意事项

通过上面的例子,我们对爬虫已经有了一个感性的认识,在编写爬虫时有以下一些注意事项:

1. 处理相对链接。有的时候我们从页面中获取的链接不是一个完整的绝对链接而是一个相对链接,这种情况下需要将其与URL前缀进行拼接(`urllib.parse`中的`urljoin()`函数可以完成此项操作)。

2. 设置代理服务。有些网站会限制访问的区域(例如美国的Netflix屏蔽了很多国家的访问),有些爬虫需要隐藏自己的身份,在这种情况下可以设置使用代理服务器,代理服务器有免费(如[西刺代理](http://www.xicidaili.com/)[快代理](https://www.kuaidaili.com/free/))和付费两种(如[讯代理](http://www.xdaili.cn/)[阿布云代理](https://www.abuyun.com/)),付费的一般稳定性和可用性都更好,可以通过`urllib.request`中的`ProxyHandler`来为请求设置代理
2. 设置代理服务。有些网站会限制访问的区域(例如美国的Netflix屏蔽了很多国家的访问),有些爬虫需要隐藏自己的身份,在这种情况下可以设置使用代理服务器,代理服务器有免费的服务器和付费的商业服务器,但后者稳定性和可用性都更好,强烈建议在商业项目中使用付费的代理服务器。可以通过修改`urllib.request`中的`ProxyHandler`来为请求设置代理服务器

3. 限制下载速度。如果我们的爬虫获取网页的速度过快,可能就会面临被封禁或者产生“损害动产”的风险(这个可能会导致吃官司且败诉),可以在两次下载之间添加延时从而对爬虫进行限速。

Expand All @@ -305,7 +314,7 @@ if __name__ == '__main__':
web_page = urllib.request.urlopen(request, context=context)
```

- 设置全局的取消证书验证
- 设置全局性取消证书验证

```Python
import ssl
Expand Down
206 changes: 152 additions & 54 deletions Day66-75/67.数据采集和解析.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,91 @@
## 数据采集和解析

通过[《网络爬虫和相关工具》](./01.网络爬虫和相关工具.md)一文,我们已经了解到了开发一个爬虫需要做的工作以及一些常见的问题,至此我们可以对爬虫开发需要做的工作以及相关的技术做一个简单的汇总,这其中可能会有一些我们之前没有使用过的第三方库,不过别担心,这些内容我们稍后都会一一讲到
通过上一个章节的讲解,我们已经了解到了开发一个爬虫需要做的工作以及一些常见的问题,下面我们给出一个爬虫开发相关技术的清单以及这些技术涉及到的标准库和第三方库,稍后我们会一一介绍这些内容

1. 下载数据 - urllib / requests / aiohttp。
2. 解析数据 - re / lxml / beautifulsoup4(bs4)/ pyquery。
3. 缓存和持久化 - pymysql / sqlalchemy / peewee/ redis / pymongo。
4. 生成数字签名 - hashlib。
5. 序列化和压缩 - pickle / json / zlib。
6. 调度器 - 进程(multiprocessing) / 线程(threading) / 协程(coroutine)。
1. 下载数据 - **urllib** / **requests** / **aiohttp**
2. 解析数据 - **re** / **lxml** / **beautifulsoup4** / **pyquery**
3. 缓存和持久化 - **pymysql** / **sqlalchemy** / **peewee**/ **redis** / **pymongo**
4. 生成数字签名 - **hashlib**
5. 序列化和压缩 - **pickle** / **json** / **zlib**
6. 调度器 - 多进程(**multiprocessing**) / 多线程(**threading**)。

### HTML页面分析
### HTML页面

```HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>Hello, world!</h1>
<p>这是一个神奇的网站!</p>
<hr>
<div>
<h2>这是一个例子程序</h2>
<p>静夜思</p>
<p class="foo">床前明月光</p>
<p id="bar">疑似地上霜</p>
<p class="foo">举头望明月</p>
<div><a href="http://www.baidu.com"><p>低头思故乡</p></a></div>
</div>
<a class="foo" href="http://www.qq.com">腾讯网</a>
<img src="./img/pretty-girl.png" alt="美女">
<img src="./img/hellokitty.png" alt="凯蒂猫">
<img src="/static/img/pretty-girl.png" alt="美女">
<table>
<tr>
<th>姓名</th>
<th>上场时间</th>
<th>得分</th>
<th>篮板</th>
<th>助攻</th>
</tr>
</table>
</body>
<html>
<head>
<title>Home</title>
<style type="text/css">
/* 此处省略层叠样式表代码 */
</style>
</head>
<body>
<div class="wrapper">
<header>
<h1>Yoko's Kitchen</h1>
<nav>
<ul>
<li><a href="" class="current">Home</a></li>
<li><a href="">Classes</a></li>
<li><a href="">Catering</a></li>
<li><a href="">About</a></li>
<li><a href="">Contact</a></li>
</ul>
</nav>
</header>
<section class="courses">
<article>
<figure>
<img src="images/bok-choi.jpg" alt="Bok Choi" />
<figcaption>Bok Choi</figcaption>
</figure>
<hgroup>
<h2>Japanese Vegetarian</h2>
<h3>Five week course in London</h3>
</hgroup>
<p>A five week introduction to traditional Japanese vegetarian meals, teaching you a selection of rice and noodle dishes.</p>
</article>
<article>
<figure>
<img src="images/teriyaki.jpg" alt="Teriyaki sauce" />
<figcaption>Teriyaki Sauce</figcaption>
</figure>
<hgroup>
<h2>Sauces Masterclass</h2>
<h3>One day workshop</h3>
</hgroup>
<p>An intensive one-day course looking at how to create the most delicious sauces for use in a range of Japanese cookery.</p>
</article>
</section>
<aside>
<section class="popular-recipes">
<h2>Popular Recipes</h2>
<a href="">Yakitori (grilled chicken)</a>
<a href="">Tsukune (minced chicken patties)</a>
<a href="">Okonomiyaki (savory pancakes)</a>
<a href="">Mizutaki (chicken stew)</a>
</section>
<section class="contact-details">
<h2>Contact</h2>
<p>Yoko's Kitchen<br>
27 Redchurch Street<br>
Shoreditch<br>
London E2 7DP</p>
</section>
</aside>
<footer>
&copy; 2011 Yoko's Kitchen
</footer>
</div>
<script>
// 此处省略JavaScript代码
</script>
</body>
</html>
```

如果你对上面的代码并不感到陌生,那么你一定知道HTML页面通常由三部分构成,分别是用来承载内容的Tag(标签)、负责渲染页面的CSS(层叠样式表)以及控制交互式行为的JavaScript。通常,我们可以在浏览器的右键菜单中通过“查看网页源代码”的方式获取网页的代码并了解页面的结构;当然,我们也可以通过浏览器提供的开发人员工具来了解网页更多的信息
如果你对上面的代码并不感到陌生,那么你一定知道HTML页面通常由三部分构成,分别是用来承载内容的Tag(标签)、负责渲染页面的CSS(层叠样式表)以及控制交互式行为的JavaScript。通常,我们可以在浏览器的右键菜单中通过“查看网页源代码”的方式获取网页的代码并了解页面的结构;当然,我们也可以通过浏览器提供的开发人员工具来了解更多的信息

#### 使用requests获取页面

Expand Down Expand Up @@ -83,28 +121,90 @@

> 说明:关于requests的详细用法可以参考它的[官方文档](http://docs.python-requests.org/zh_CN/latest/user/quickstart.html)
### 四种采集方式
### 页面解析

#### 四种采集方式的比较
#### 几种解析方式的比较

| 抓取方法 | 速度 | 使用难度 | 备注 |
| ------------- | ------------------------- | -------- | ------------------------------------------ |
| 正则表达式 || 困难 | 常用正则表达式<br>在线正则表达式测试 |
| lxml || 一般 | 需要安装C语言依赖库<br>唯一支持XML的解析器 |
| BeautifulSoup | 较快/较慢(取决于解析器) | 简单 | |
| PyQuery | 较快 | 简单 | Python版的jQuery |
| 解析方式 | 对应的模块 | 速度 | 使用难度 | 备注 |
| -------------- | ------------- | ------ | -------- | ------------------------------------------- |
| 正则表达式解析 | re || 困难 | 常用正则表达式<br/>在线正则表达式测试 |
| XPath解析 | lxml || 一般 | 需要安装C语言依赖库<br/>唯一支持XML的解析器 |
| CSS选择器解析 | bs4 / pyquery | 不确定 | 简单 | |

> 说明:BeautifulSoup可选的解析器包括:Python标准库(html.parser)、lxml的HTML解析器、lxml的XML解析器和html5lib。
#### 使用正则表达式
#### 使用正则表达式解析页面

如果你对正则表达式没有任何的概念,那么推荐先阅读[《正则表达式30分钟入门教程》](),然后再阅读我们之前讲解在Python中如何使用正则表达式一文。

#### 使用XPath和Lxml


#### XPath解析和lxml

#### BeautifulSoup的使用
XPath是在XML文档中查找信息的一种语法,它使用路径表达式来选取XML文档中的节点或者节点集。这里所说的XPath节点包括元素、属性、文本、命名空间、处理指令、注释、根节点等。

```XML
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book>
<title lang="eng">Harry Potter</title>
<price>29.99</price>
</book>
<book>
<title lang="eng">Learning XML</title>
<price>39.95</price>
</book>
</bookstore>
```
对于上面的XML文件,我们可以用如下所示的XPath语法获取文档中的节点。

| 路径表达式 | 结果 |
| --------------- | ------------------------------------------------------------ |
| bookstore | 选取 bookstore 元素的所有子节点。 |
| /bookstore | 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
| bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。 |
| //book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
| bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
| //@lang | 选取名为 lang 的所有属性。 |

在使用XPath语法时,还可以使用XPath中的谓词。

| 路径表达式 | 结果 |
| ---------------------------------- | ------------------------------------------------------------ |
| /bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
| /bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
| /bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
| /bookstore/book[position()<3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
| //title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
| //title[@lang='eng'] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
| /bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
| /bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |

XPath还支持通配符用法,如下所示。

| 路径表达式 | 结果 |
| ------------ | --------------------------------- |
| /bookstore/* | 选取 bookstore 元素的所有子元素。 |
| //* | 选取文档中的所有元素。 |
| //title[@*] | 选取所有带有属性的 title 元素。 |

如果要选取多个节点,可以使用如下所示的方法。

| 路径表达式 | 结果 |
| -------------------------------- | ------------------------------------------------------------ |
| //book/title \| //book/price | 选取 book 元素的所有 title 和 price 元素。 |
| //title \| //price | 选取文档中的所有 title 和 price 元素。 |
| /bookstore/book/title \| //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 |

> 说明:上面的例子来自于菜鸟教程网站上[XPath教程](<https://www.runoob.com/xpath/xpath-tutorial.html>),有兴趣的读者可以自行阅读原文。
当然,如果不理解或者不太熟悉XPath语法,可以在Chrome浏览器中按照如下所示的方法查看元素的XPath语法。

![](./res/douban-xpath.png)



### BeautifulSoup的使用

BeautifulSoup是一个可以从HTML或XML文件中提取数据的Python库。它能够通过你喜欢的转换器实现惯用的文档导航、查找、修改文档的方式。

Expand All @@ -121,12 +221,10 @@ BeautifulSoup是一个可以从HTML或XML文件中提取数据的Python库。它

> 说明:更多内容可以参考BeautifulSoup的[官方文档](https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html)
#### PyQuery的使用
### PyQuery的使用

pyquery相当于jQuery的Python实现,可以用于解析HTML网页。



### 实例 - 获取知乎发现上的问题链接

```Python
Expand Down
Loading

0 comments on commit d9ec811

Please sign in to comment.