Dust8 的博客

读书百遍其义自见

0%

最近过了一遍《HTML & CSS Design and Build Websites》.这本书对初学者比较友好,
图文并茂,排版美观,内容全面,是一本值得一看的书.主要是自学的,不懂的就搜索,没有完整的看过一遍,看这本书主要是看看有哪些漏下的,还有就是顺便复习一下.
这里推荐一个css可视化的参考网站,里面用图文来解释css的属性,非常直观: css reference.

一些知识

颜色

色调

差不多就是颜色.

色环

就是把颜色环成一个圆圈,每一个角度对应一个色调.

饱和度

是指一种色调中灰色的含量,用百分比来表示.100%的饱和度代表不加入灰色.同样0%的饱和度就全是灰色了.

亮度

是指色调中加入黑色的含量.亮度最大时就是该色调,亮度最小时就是黑色了.

明度

是指色调中加入白色或者黑色的含量.明度为0%时是黑色,50%时是标准色,100%时是白色.

line-height

行间距就是2行直接的距离.在css中有一点不一样, line-height 指的是字体大小再加行间距. 这就是 line-heightfont-size 的区别.推荐用 1.4em1.5em,这样可以提高文本的可读性.

vertical-align

垂直对齐.比如说, 要实现顶部对齐就可以设置 vertical-align: top.

自定义text inputs 的格式

比如说给它前面加个图标:

  • 设置 background-image, background-repeat:no-repeat, background-postion,
    这样图标就在 input 里面了
  • 设置 padding-left, 这样就可以不让图标盖住了输入的数据

960 栅格

因为以前大部分设备显示都大于 960px, 所以选960px来显示内容.在把960px
分成12列,每一列是 80px, 这个80px的列包含左右各有10px的间隔,设置width
的时候就可以把内容设置为几个列的宽度.比如说, 一列放置3个内容,那么每个内容就
占4个列, 80(一个列的宽度)4-10(一个间隔的宽度)2 就是每个内容块的宽度,
所以每个内容块的宽度就是 300px .这样就可以使得整个页面非常整洁.

线框图

是指网站中每个页面内容的概要.显示出信息的层次结构以及需要占用的空间.在写代码前
画下线框图能更省时间,这样也能更好的分离任务,一次只关注一点,效率才高.

seo

站内优化

  • 页面标题, <title></title>
  • url
  • 标题, <h1><h6>
  • 正文
  • 链接文本
  • 图像的alt属性
  • 页面描述

说明

unsplash 网站比较慢, 只爬数据就用了30分钟, 爬了945页,每页有24条数据. 尝试下载图片,更加慢, 平均每张图片大约10M, 如果要下载完全部图片大约要 9452410/1024 = 222G, 还是算了吧.

爬虫代码

根据 aosabook/500lines 的爬虫修改而来, 尝试使用异步来写爬虫.
使用chrome浏览器开发工具来分析入口,在 network 里面的 xhr 就可以看到了, 还可以看到请求的头部,因为该网站需要 authorization 才可以爬取.

crawl.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import asyncio
import crawling


def main():
loop = asyncio.get_event_loop()
crawler = crawling.Crawler()

try:
loop.run_until_complete(crawler.crawl())
except KeyboardInterrupt:
print('\nInterrupted\n')
finally:
crawler.report()
crawler.close()

loop.stop()
loop.run_forever()
loop.close()


if __name__ == '__main__':
main()

crawling.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
import aiohttp
import asyncio
import time
import urllib.parse
from db import insert_many


def is_redirect(response):
return response.status in (300, 301, 301, 303, 307)


class Crawler:
def __init__(self,
max_redirect=10,
max_tries=4,
max_tasks=10,
*,
loop=None):
self.max_redirect = max_redirect
self.max_tries = max_tries
self.max_tasks = max_tasks
self.loop = loop or asyncio.get_event_loop()

self.q = asyncio.Queue(loop=self.loop)
self.seen_urls = set()
self.session = aiohttp.ClientSession(loop=self.loop)
self.headers = {
'authorization':
'Client-ID d69927c7ea5c770fa2ce9a2f1e3589bd896454f7068f689d8e41a25b54fa6042'
}
self.base_url = 'https://unsplash.com/napi/photos?per_page=24&order_by=latest&page=%s'
self.page = 1
self.add_url(self.base_url % (1))

self.t0 = None
self.t1 = None

def add_url(self, url, max_redirect=None):
if max_redirect is None:
max_redirect = self.max_redirect
self.seen_urls.add(url)
self.q.put_nowait((url, max_redirect))

async def crawl(self):
workers = [
asyncio.Task(self.work(), loop=self.loop)
for _ in range(self.max_tasks)
]
self.t0 = time.time()
await self.q.join()
self.t1 = time.time()
for w in workers:
w.cancel()

def report(self):
print('total time: ', self.t1 - self.t0)
print('total page:', self.page)

async def work(self):
try:
while True:
url, max_redirect = await self.q.get()
await self.fetch(url, max_redirect)
self.q.task_done()
except asyncio.CancelledError:
pass

async def fetch(self, url, max_redirect):
tries = 0
while tries < self.max_tries:
try:
response = await self.session.get(
url, headers=self.headers, allow_redirects=False)
break
except aiohttp.ClientError:
pass
tries += 1
else:
# all tries failed.
return

try:
if is_redirect(response):
location = response.headers['location']
next_url = urllib.parse.urljoin(url, location)
if next_url in self.seen_urls:
return
if max_redirect > 0:
self.add_url(next_url, max_redirect - 1)
else:
links = await self.parse_links(response)
for link in links.difference(self.seen_urls):
self.q.put_nowait((link, self.max_redirect))
self.seen_urls.update(links)
finally:
await response.release()

async def parse_links(self, response):
links = set()

if response.status == 200:
data = await response.json()
if data:
insert_many(data)
self.page += 1
print('next', self.base_url % (self.page))
links.add(self.base_url % (self.page))

return links

def close(self):
self.session.close()

请求回来的数据比较乱,还是用 mongdb 存储比较方便
db.py

1
2
3
4
5
6
7
8
9
import pymongo

client = pymongo.MongoClient()
db = client.unsplash
photo = db.photo


def insert_many(photos):
photo.insert_many(photos)

数据分析

提取数据

1
2
3
4
5
6
7
8
import pymongo

client = pymongo.MongoClient()
db = client.unsplash
photo = db.photo
photos = []
for p in photo.find():
photos.append(p)

数据总量

len(photos)  

22648

定义常用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from collections import defaultdict

def get_counts(sequence):
counts = defaultdict(int)
for x in sequence:
counts[x] += 1
return counts

def top_counts(count_dict, n=10):
value_key_pairs = [(count, key) for key, count in count_dict.items()]
value_key_pairs.sort()
value_key_pairs_n = value_key_pairs[-n:]
value_key_pairs_n.sort(reverse=True)
return value_key_pairs_n

top10 图片大小

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
width_heights = [str(p['width'])+'_'+str(p['height']) for p in photos]
width_heights_counts = get_counts(width_heights)
print(len(width_heights_counts))
top_counts(width_heights_counts)
```

7891
[(1751, '5184_3456'),
(1132, '6000_4000'),
(999, '5472_3648'),
(575, '5760_3840'),
(462, '6016_4016'),
(427, '5616_3744'),
(404, '4896_3264'),
(392, '4928_3264'),
(321, '3264_2448'),
(316, '4288_2848')]

### top10 颜色
```python
colors = [p['color'] for p in photos if 'color' in p and p['color'] is not None]
colors_counts = get_counts(colors)
print(len(colors_counts))
top_counts(colors_counts)
```

19434
[(119, '#FFFFFF'),
(73, '#FEFEFE'),
(45, '#010101'),
(42, '#FCFCFC'),
(41, '#FDFDFD'),
(41, '#FBFBFB'),
(29, '#020202'),
(28, '#F9F9F9'),
(24, '#FAFAFA'),
(22, '#FEFEFD')]

### top10 每年照片总数
```python
created_ats = [ p['created_at'][:4] for p in photos]
created_ats_counts = get_counts(created_ats)
top_counts(created_ats_counts)
[(12761, '2016'),
 (5467, '2015'),
 (2702, '2017'),
 (1498, '2014'),
 (220, '2013')]

起因

最近写 scrapy 时发现,继承 spider 时重写 __init__ 后设置属性,该属性无法访问,会引起 AttributeError。 以前也在一些 orm 包里重写
__init__ 时出现该问题。百度+谷歌后还是无果,也许我用的都是假的。翻翻源代码,发现 __slots__ 比较可疑。

官方文档翻译

slots

默认情况下,类的实例有一个储存属性的字典。该字典会消耗那些只有少量实例变量的对象的空间。
当创建大量实例时,空间消耗可能变得非常严重。

可以通过在类的定义中,定义 __slots__ 来覆盖默认值。__slots__ 声明一个实例变量的序列,
并且为每个实例保留足够的空间来保存每个变量的值。因为 __dict__ 不会为每一个实例创建,
所以可以节省空间。

object.slots

这个类变量声明是由实例变量名字组成的字符串,可迭代对象,或者字符串序列。
__slots__ 为已声明的变量预留空间,并阻止为每个实例自动创建 __dict____weakref__

使用 slots 时的注意事项

  • 当从没有 __slots__ 的类继承时,该类的 __dict__ 属性将始终可访问,因此子类中的 __slots__ 定义是无意义的。
  • 如果没有 __dict__ 变量,则不能为实例指定 __slots__ 定义中未列出的新变量。 尝试分配到未列出的变量名称会引发 AttributeError。 如果需要动态分配新变量,则在 __slots__ 声明中的字符串序列中添加 __dict__
  • 如果每个实例没有 __weakref__ 变量,定义 __slots__ 的类就不支持对其实例的弱引用。 如果需要弱引用支持,请在 __slots__ 声明中的字符串序列中添加 __weakref__
  • __slots__ 在类级别通过为每个变量名创建描述符(实现描述符)来实现。 因此,类属性不能用于为 __slots__ 定义的实例变量设置默认值, 否则,类属性将覆盖描述符。
  • __slots__ 声明的操作仅限于定义它的类。 因此,子类将有一个 __dict__ ,除非子类还定义 __slots__(其中只能包含任何其他 slots 的名字)。
  • 如果一个类定义了一个在基类中定义的 slots,那么由基类 slots 定义的实例变量是不可访问的(除了直接从基类检索它的描述符)。 这使得程序的含义未定义。 将来,可能会添加一个检查来防止这种情况。
  • 非空 __slots__ 不适用于从“可变长度”内置类型(如 intbytestuple)派生的类。
  • 任何非字符串可迭代可以分配给 __slots__。 也可以使用映射, 然而,将来,可以对与每个键相对应的值分配特殊的含义。
  • __class__ 赋值只有当两个类都有相同的 __slots__是才有效。

总结

__slots__ 有2大用途: 一个是节省空间(大量实例时才明显),另一个是加快操作实例的速度(官方没说)。至于本文开头说的问题,暂时还没想明白,以后补上。
好吧,最后发现是 __init__ 写成 __int__ 了,我也是醉了。

起因

在资讯爆炸的时代,如何挑选有用的资讯就比较困难,所以有了不少筛选信息的网站.
在专注于编程方面的, 又比较活跃和大一些的就有 稀土掘金, 开发者头条 和 推酷.
前2者比较新比较活跃, 后者比较老.
这里要抱怨下常用的套路, 从免登录+无广告,用户多一点就要登录,到登录后要验证个人信息+加入广告.
我个人非常不喜欢这种要强制要登录的.我只是单纯的想看一下文章而已, 完全没必要去登录.
稀土掘金现在有些文章就需要登录才能展开全文, 所以就要想想办法,
开发了 gold full 插件.

原理

按照经验来说,一般全文都已经请求到客户端了,只是被隐藏了而已. 这样可以省去重复请求, 节约服务器资源.
先看网页源代码,完全没有数据.在审查元素, 可以看到全文都在里面,只是没有显示出来.

分析它的 展开全文 按钮的 js 代码,发现自己太菜,看不懂.只好查看内容的 css 类名,
终于有所发现. 有个 hidden 类, 有 overflow: hidden;position: relative; 属性,
overflow 属性去掉之后就可以看到全文. 不过有个问题会出现和下面的元素内容重叠.
可以想到是高度问题,发现有个 max-height,把它去掉就完全没有问题了.

开发插件

每次都去改属性比较麻烦,可以做成插件,来自动完成.
Chrome扩展及应用开发
What are extensions
有这本书和官方文档就可以开发插件了.
goldfull 代码很简单,改了 max-heightnone, 移除了 展开全文 按钮.

总结

今天总算没有浪费在游戏上面,然后就是把知识用起来的感觉真好.

起因

自学编程不少年了,看过不少东西,觉得什么都知道一点,让自己做出个项目却完全没有头绪.
所以就决定刷完 freeCodeCamp,打好基础.在 Basic Algorithm Scripting碰到一个问题
Algorithm:Title Case a Sentence 完全没有头绪.如果是python用内置的方法就可以实现了,
javascript 里面没有.点击Read-Search-Ask就可以看到一些解决方法.

用最基础的方式解决

这种方式比较简单, 不过代码比较长.

String.prototype.replaceAt = function(index, character) {
    return this.substr(0, index) + character + this.substr(index+character.length);
};


function titleCase(str) {
    var newTitle = str.split(' ');
    var updatedTitle = [];
    for (var st in newTitle) {
        updatedTitle[st] = newTitle[st].toLowerCase().replaceAt(0, newTitle[st].charAt(0).toUpperCase());
    }
    return updatedTitle.join(' ');
}

用内置的高阶方法解决

利用的语言内置的高阶函数,可以减少很多代码,例如 map.

function titleCase(str) {
    var convertToArray = str.toLowerCase().split(" ");
    var result = convertToArray.map(function(val){
      return val.replace(val.charAt(0), val.charAt(0).toUpperCase());
    });
    return result.join(" ");
}

titleCase("I'm a little tea pot");

利用各种黑魔法解决

这种方法比较难想到,也不太通用.

function titleCase(str) {
    return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase());
}

总结

其实这三种方法适用各种语言,代表着你掌握语言的深入程度.写代码要优先使用第二种方法.
因为它可以减少代码量,又容易阅读.因为好的代码要易读.不然过段时间连你自己都不知道为什么这么写了.