Commit 402e0564 authored by jackfrued's avatar jackfrued

更新了文档

parent 822ffd00
...@@ -342,6 +342,9 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -342,6 +342,9 @@ Type "help", "copyright", "credits" or "license" for more information.
>>> >>>
>>> Dept.objects.filter(no__gt=10).filter(no__lt=40) # 查询部门编号大于10小于40的部门 >>> Dept.objects.filter(no__gt=10).filter(no__lt=40) # 查询部门编号大于10小于40的部门
<QuerySet [<Dept: 销售1>, <Dept: 运维1>]> <QuerySet [<Dept: 销售1>, <Dept: 运维1>]>
>>>
>>> Dept.objects.filter(no__range=(10, 30)) # 查询部门编号在10到30之间的部门
<QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>]>
``` ```
查询单个对象。 查询单个对象。
...@@ -350,8 +353,10 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -350,8 +353,10 @@ Type "help", "copyright", "credits" or "license" for more information.
>>> >>>
>>> Dept.objects.get(pk=10) >>> Dept.objects.get(pk=10)
<Dept: 研发1> <Dept: 研发1>
>>>
>>> Dept.objects.get(no=20) >>> Dept.objects.get(no=20)
<Dept: 销售1> <Dept: 销售1>
>>>
>>> Dept.objects.get(no__exact=30) >>> Dept.objects.get(no__exact=30)
<Dept: 运维1> <Dept: 运维1>
``` ```
...@@ -362,6 +367,7 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -362,6 +367,7 @@ Type "help", "copyright", "credits" or "license" for more information.
>>> >>>
>>> Dept.objects.order_by('no') # 查询所有部门按部门编号升序排列 >>> Dept.objects.order_by('no') # 查询所有部门按部门编号升序排列
<QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>, <Dept: 研发3>]> <QuerySet [<Dept: 研发1>, <Dept: 销售1>, <Dept: 运维1>, <Dept: 研发3>]>
>>>
>>> Dept.objects.order_by('-no') # 查询所有部门按部门编号降序排列 >>> Dept.objects.order_by('-no') # 查询所有部门按部门编号降序排列
<QuerySet [<Dept: 研发3>, <Dept: 运维1>, <Dept: 销售1>, <Dept: 研发1>]> <QuerySet [<Dept: 研发3>, <Dept: 运维1>, <Dept: 销售1>, <Dept: 研发1>]>
``` ```
...@@ -372,6 +378,7 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -372,6 +378,7 @@ Type "help", "copyright", "credits" or "license" for more information.
>>> >>>
>>> Dept.objects.order_by('no')[0:2] # 按部门编号排序查询1~2部门 >>> Dept.objects.order_by('no')[0:2] # 按部门编号排序查询1~2部门
<QuerySet [<Dept: 研发1>, <Dept: 销售1>]> <QuerySet [<Dept: 研发1>, <Dept: 销售1>]>
>>>
>>> Dept.objects.order_by('no')[2:4] # 按部门编号排序查询3~4部门 >>> Dept.objects.order_by('no')[2:4] # 按部门编号排序查询3~4部门
<QuerySet [<Dept: 运维1>, <Dept: 研发3>]> <QuerySet [<Dept: 运维1>, <Dept: 研发3>]>
``` ```
...@@ -382,37 +389,43 @@ Type "help", "copyright", "credits" or "license" for more information. ...@@ -382,37 +389,43 @@ Type "help", "copyright", "credits" or "license" for more information.
>>> >>>
>>> Emp.objects.filter(dept__no=10) # 根据部门编号查询该部门的员工 >>> Emp.objects.filter(dept__no=10) # 根据部门编号查询该部门的员工
<QuerySet [<Emp: 乔峰>, <Emp: 张无忌>, <Emp: 张三丰>]> <QuerySet [<Emp: 乔峰>, <Emp: 张无忌>, <Emp: 张三丰>]>
>>>
>>> Emp.objects.filter(dept__name__contains='销售') # 查询名字包含“销售”的部门的员工 >>> Emp.objects.filter(dept__name__contains='销售') # 查询名字包含“销售”的部门的员工
<QuerySet [<Emp: 黄蓉>]> <QuerySet [<Emp: 黄蓉>]>
>>>
>>> Dept.objects.get(pk=10).emp_set.all() # 通过部门反查部门所有的员工 >>> Dept.objects.get(pk=10).emp_set.all() # 通过部门反查部门所有的员工
<QuerySet [<Emp: 乔峰>, <Emp: 张无忌>, <Emp: 张三丰>]> <QuerySet [<Emp: 乔峰>, <Emp: 张无忌>, <Emp: 张三丰>]>
``` ```
> 说明:由于员工与部门之间存在外键关联,所以也能通过部门反向查询该部门的员工(从一对多关系中“一”的一方查询“多”的一方),默认情况下反查属性名是`类名小写_set`(例子中的`emp_set`),当然也可以在创建模型时通过`related_name`指定反查属性的名字。 > 说明1:由于员工与部门之间存在多对一外键关联,所以也能通过部门反向查询该部门的员工(从一对多关系中“一”的一方查询“多”的一方),反向查询属性默认的名字是`类名小写_set`(如上面例子中的`emp_set`),当然也可以在创建模型时通过`ForeingKey`的`related_name`属性指定反向查询属性的名字。如果不希望执行反向查询可以将`related_name`属性设置为`'+'`或以`'+'`开头的字符串。
> 说明2:查询多个对象的时候返回的是QuerySet对象,QuerySet使用了惰性查询,即在创建QuerySet对象的过程中不涉及任何数据库活动,等真正用到对象时(求值QuerySet)才向数据库发送SQL语句并获取对应的结果,这一点在实际开发中需要引起注意!
> 说明3:可以在QuerySet上使用`update()`方法一次更新多个对象。
#### 删除 #### 删除
```Shell ```Shell
>>>
>>> Dept.objects.get(pk=40).delete()
(1, {'hrs.Dept': 1})
``` ```
最后,我们通过上面掌握的知识来实现部门展示以及根据部门获取部门对应员工信息的功能,效果如下图所示,对应的代码可以访问<>
### Django模型最佳实践 ### Django模型最佳实践
1. 正确的模型命名和关系字段命名。 1. 正确的模型命名和关系字段命名。
2. 设置适当的related_name属性。 2. 设置适当的`related_name`属性。
3.OneToOneField代替ForeignKeyField(unique=True) 3. 用`OneToOneField`代替`ForeignKeyField(unique=True)`
4. 通过迁移操作来添加模型。 4. 通过“迁移操作”(migrate)来添加模型。
5. 用NoSQL来应对需要降低范式级别的场景。 5. 用NoSQL来应对需要降低范式级别的场景。
6. 如果布尔类型可以为空要使用NullBooleanField 6. 如果布尔类型可以为空要使用`NullBooleanField`
7. 在模型中放置业务逻辑。 7. 在模型中放置业务逻辑。
8.ModelName.DoesNotExists取代ObjectDoesNotExists 8. 用`<ModelName>.DoesNotExists`取代`ObjectDoesNotExists`
9. 在数据库中不要出现无效数据。 9. 在数据库中不要出现无效数据。
10. 不要对QuerySet调用len函数。 10. 不要对`QuerySet`调用`len()`函数。
11.QuerySet的exists()方法的返回值用于if条件。 11. 将`QuerySet`的`exists()`方法的返回值用于`if`条件。
12.DecimalField来存储货币相关数据而不是FloatField 12. 用`DecimalField`来存储货币相关数据而不是`FloatField`
13. 定义\_\_str\_\_方法。 13. 定义`__str__`方法。
14. 不要将数据文件放在同一个目录中。 14. 不要将数据文件放在同一个目录中。
> 说明:以上内容来自于STEELKIWI网站的[*Best Practice working with Django models in Python*](https://steelkiwi.com/blog/best-practices-working-django-models-python/),有兴趣的小伙伴可以阅读原文。 > 说明:以上内容来自于STEELKIWI网站的[*Best Practice working with Django models in Python*](https://steelkiwi.com/blog/best-practices-working-django-models-python/),有兴趣的小伙伴可以阅读原文。
...@@ -513,22 +526,31 @@ ManyToManyField属性 ...@@ -513,22 +526,31 @@ ManyToManyField属性
| verbose_name | 为对象设定人类可读的名称 | | verbose_name | 为对象设定人类可读的名称 |
| verbose_name_plural | 设定对象的复数名称 | | verbose_name_plural | 设定对象的复数名称 |
### 数据库API参考 ### 查询参考
按字段查找可以用的条件:
1. exact / iexact:精确匹配/忽略大小写的精确匹配查询
2. contains / icontains / startswith / istartswith / endswith / iendswith:基于`like`的模糊查询
3. in:集合运算
4. gt / gte / lt / lte:大于/大于等于/小于/小于等于关系运算
5. range:指定范围查询(SQL中的`between…and…`)
6. year / month / day / week_day / hour / minute / second:查询时间日期
7. isnull:查询空值(True)或非空值(False)
8. search:基于全文索引的全文检索
9. regex / iregex:基于正则表达式的模糊匹配查询
Q对象(用于执行复杂查询)的使用:
```Shell
>>>
>>> from django.db.models import Q
>>> Emp.objects.filter(
... Q(name__startswith='张'),
... Q(sal__gte=5000) | Q(comm__gte=1000)
... ) # 查询名字以“张”开头 工资大于等于5000或补贴大于等于1000的员工
<QuerySet [<Emp: 张三丰>]>
```
按字段查找可以用的条件:
1. exact / iexact
2. contains / icontains
3. in
4. gt / gte / lt / lte
5. startswith / istartswith / endswith / iendswith
6. range
7. year / month / day / week_day / hour / minute / second
8. isnull
9. search
10. regex / iregex
跨关系查找
## 存储数据
### 缓存和持久化
### 磁盘文件缓存
### 数据库缓存
## 数据采集和解析
### HTML页面分析
### 三种采集方式
## 网络爬虫和相关工具
### 网络爬虫
网络爬虫(web crawler),以前经常称之为网络蜘蛛(spider),是按照一定的规则自动浏览万维网并获取信息的机器人程序(或脚本),曾经被广泛的应用于互联网搜索引擎。使用过互联网和浏览器的人都知道,网页中除了供用户阅读的文字信息之外,还包含一些超链接。网络爬虫系统正是通过网页中的超链接信息不断获得网络上的其它页面。正因如此,网络数据采集的过程就像一个爬虫或者蜘蛛在网络上漫游,所以才被形象的称为网络爬虫或者网络蜘蛛。
#### 爬虫的应用领域
在理想的状态下,所有ICP(Internet Content Provider)都应该为自己的网站提供API接口来共享它们允许其他程序获取的数据,在这种情况下爬虫就不是必需品,国内比较有名的电商平台(如淘宝、京东等)、社交平台(如腾讯微博等)等网站都提供了自己的Open API,但是这类Open API通常会对可以抓取的数据以及抓取数据的频率进行限制。对于大多数的公司而言,及时的获取行业相关数据是企业生存的重要环节之一,然而大部分企业在行业数据方面的匮乏是其与生俱来的短板,合理的利用爬虫来获取数据并从中提取出有价值的信息是至关重要的。当然爬虫还有很多重要的应用领域,以下列举了其中的一部分:
1. 搜索引擎
2. 新闻聚合
3. 社交应用
4. 舆情监控
5. 行业数据
### 合法性和背景调研
#### 爬虫合法性探讨
1. 网络爬虫领域目前还属于拓荒阶段,虽然互联网世界已经通过自己的游戏规则建立起一定的道德规范(Robots协议,全称是“网络爬虫排除标准”),但法律部分还在建立和完善中,也就是说,现在这个领域暂时还是灰色地带。
2. “法不禁止即为许可”,如果爬虫就像浏览器一样获取的是前端显示的数据(网页上的公开信息)而不是网站后台的私密敏感信息,就不太担心法律法规的约束,因为目前大数据产业链的发展速度远远超过了法律的完善程度。
3. 在爬取网站的时候,需要限制自己的爬虫遵守Robots协议,同时控制网络爬虫程序的抓取数据的速度;在使用数据的时候,必须要尊重网站的知识产权(从Web 2.0时代开始,虽然Web上的数据很多都是由用户提供的,但是网站平台是投入了运营成本的,当用户在注册和发布内容时,平台通常就已经获得了对数据的所有权、使用权和分发权)。如果违反了这些规定,在打官司的时候败诉几率相当高。
#### Robots.txt文件
大多数网站都会定义robots.txt文件,下面以淘宝的[robots.txt](http://www.taobao.com/robots.txt)文件为例,看看该网站对爬虫有哪些限制。
```
User-agent: Baiduspider
Allow: /article
Allow: /oshtml
Disallow: /product/
Disallow: /
User-Agent: Googlebot
Allow: /article
Allow: /oshtml
Allow: /product
Allow: /spu
Allow: /dianpu
Allow: /oversea
Allow: /list
Disallow: /
User-agent: Bingbot
Allow: /article
Allow: /oshtml
Allow: /product
Allow: /spu
Allow: /dianpu
Allow: /oversea
Allow: /list
Disallow: /
User-Agent: 360Spider
Allow: /article
Allow: /oshtml
Disallow: /
User-Agent: Yisouspider
Allow: /article
Allow: /oshtml
Disallow: /
User-Agent: Sogouspider
Allow: /article
Allow: /oshtml
Allow: /product
Disallow: /
User-Agent: Yahoo! Slurp
Allow: /product
Allow: /spu
Allow: /dianpu
Allow: /oversea
Allow: /list
Disallow: /
User-Agent: *
Disallow: /
```
注意上面robots.txt第一段的最后一行,通过设置“Disallow: /”禁止百度爬虫访问除了“Allow”规定页面外的其他所有页面。因此当你在百度搜索“淘宝”的时候,搜索结果下方会出现:“由于该网站的robots.txt文件存在限制指令(限制搜索引擎抓取),系统无法提供该页面的内容描述”。百度作为一个搜索引擎,至少在表面上遵守了淘宝网的robots.txt协议,所以用户不能从百度上搜索到淘宝内部的产品信息。
![](./res/baidu-search-taobao.png)
### 相关工具介绍
#### HTTP协议
#### 相关工具
1. Chrome Developer Tools
![](./res/chrome-developer-tools.png)
2. POSTMAN
![](./res/postman.png)
3. HTTPie
```Shell
$ http --header http://www.scu.edu.cn
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, max-age=600
Connection: Keep-Alive
Content-Encoding: gzip
Content-Language: zh-CN
Content-Length: 14403
Content-Type: text/html
Date: Sun, 27 May 2018 15:38:25 GMT
ETag: "e6ec-56d3032d70a32-gzip"
Expires: Sun, 27 May 2018 15:48:25 GMT
Keep-Alive: timeout=5, max=100
Last-Modified: Sun, 27 May 2018 13:44:22 GMT
Server: VWebServer
Vary: User-Agent,Accept-Encoding
X-Frame-Options: SAMEORIGIN
```
4. BuiltWith:识别网站使用的技术
```Python
>>>
>>> import builtwith
>>> builtwith.parse('http://www.bootcss.com/')
{'web-servers': ['Nginx'], 'font-scripts': ['Font Awesome'], 'javascript-frameworks': ['Lo-dash', 'Underscore.js', 'Vue.js', 'Zepto', 'jQuery'], 'web-frameworks': ['Twitter Bootstrap']}
>>>
>>> import ssl
>>> ssl._create_default_https_context = ssl._create_unverified_context
>>> builtwith.parse('https://www.jianshu.com/')
{'web-servers': ['Tengine'], 'web-frameworks': ['Twitter Bootstrap', 'Ruby on Rails'], 'programming-languages': ['Ruby']}
```
5. python-whois:查询网站的所有者
```Python
>>>
>>> import whois
>>> whois.whois('baidu.com')
{'domain_name': ['BAIDU.COM', 'baidu.com'], 'registrar': 'MarkMonitor, Inc.', 'whois_server': 'whois.markmonitor.com', 'referral_url': None, 'updated_date': [datetime.datetime(2017, 7, 28, 2, 36, 28), datetime.datetime(2017, 7, 27, 19, 36, 28)], 'creation_date': [datetime.datetime(1999, 10, 11, 11, 5, 17), datetime.datetime(1999, 10, 11, 4, 5, 17)], 'expiration_date': [datetime.datetime(2026, 10, 11, 11, 5, 17), datetime.datetime(2026, 10, 11, 0, 0)], 'name_servers': ['DNS.BAIDU.COM', 'NS2.BAIDU.COM', 'NS3.BAIDU.COM', 'NS4.BAIDU.COM', 'NS7.BAIDU.COM', 'dns.baidu.com', 'ns4.baidu.com', 'ns3.baidu.com', 'ns7.baidu.com', 'ns2.baidu.com'], 'status': ['clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited', 'clientTransferProhibited https://icann.org/epp#clientTransferProhibited', 'clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited', 'serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited', 'serverTransferProhibited https://icann.org/epp#serverTransferProhibited', 'serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited', 'clientUpdateProhibited (https://www.icann.org/epp#clientUpdateProhibited)', 'clientTransferProhibited (https://www.icann.org/epp#clientTransferProhibited)', 'clientDeleteProhibited (https://www.icann.org/epp#clientDeleteProhibited)', 'serverUpdateProhibited (https://www.icann.org/epp#serverUpdateProhibited)', 'serverTransferProhibited (https://www.icann.org/epp#serverTransferProhibited)', 'serverDeleteProhibited (https://www.icann.org/epp#serverDeleteProhibited)'], 'emails': ['abusecomplaints@markmonitor.com', 'whoisrelay@markmonitor.com'], 'dnssec': 'unsigned', 'name': None, 'org': 'Beijing Baidu Netcom Science Technology Co., Ltd.', 'address': None, 'city': None, 'state': 'Beijing', 'zipcode': None, 'country': 'CN'}
```
6. robotparser:解析robots.txt的工具
### 一个简单的爬虫
构造一个爬虫一般分为数据采集、数据处理和数据存储三个部分的内容。
首先我们要设定抓取的目标并获取网页。
1. 设置重试次数。
2. 设置用户代理。
...@@ -249,7 +249,7 @@ ...@@ -249,7 +249,7 @@
### Day66~75 - [爬虫](./Day66-75) ### Day66~75 - [爬虫](./Day66-75)
#### Day66 - [爬虫简介和相关工具](./Day66-75/爬虫简介和相关工具.md) #### Day66 - [网络爬虫和相关工具](./Day66-75/网络爬虫和相关工具.md)
#### Day67 - [数据采集和解析](./Day66-75/数据采集和解析.md) #### Day67 - [数据采集和解析](./Day66-75/数据采集和解析.md)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment