因为数据库要升级到 MySQL 8.0 所以要对架构做升级,主要的修改工作如下:
驱动包要升级为 mysql-connector-java-8.0.11.jar
JDBC driver 由 com.mysql.jdbc.Driver 改为 com.mysql.cj.jdbc.Driver
JDBC url 中加上 userSSL=false
升级以后发现通过 Mybatisplus 从数据库获取的时间字段值和数据库的值会有几小时的差异,这其实是程序所在时区和数据库的时区不一致导致的。
解决方案有两个
通过代码将程序的系统默认时区改成和数据库的时区一致,如果数据库时区为0时区,那需要将程序的系统时区改成0时区。
通过在JDBC url 增加参数,使数据库时区和程序系统时区一致:
如果你是在本地运行代码,在 JDBC url 加上 &serverTimezone=Asia/Shanghai 或者 &serverTimezone=GMT%2B8
如果你的程序是在服务器上运行,则加上&serverTimezone={服务器的时区},0时区的话就是UTC。
]]>文章标题:连接 MySQL 8.0 的时区问题
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2021/09/30/mysql8-timezone/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
金蝶云上传文件的结果会以image类型保存在SqlServer数据库,现在希望用代码的方式把这部分文件批量导出
|
|
当我们在终端执行命令行时,如果遇到带空格的路径名,往往有两种处理方法
|
|
而当我们在Java使用 Runtime.getRuntime().exec()
执行上面的命令时,却得不到想要的结果
想要解决该问题,首先分析 exec(String cmd)
方法的源码
|
|
public StringTokenizer(String str) {
this(str, “ \t\n\r\f”, false);
}
public StringTokenizer(String str, String delim, boolean returnDelims) {
currentPosition = 0;
newPosition = -1;
delimsChanged = false;
this.str = str;
maxPosition = str.length();
delimiters = delim;
retDelims = returnDelims;
setMaxDelimCodePoint();
}
String[] cmdarray = new String[]{“cat”, “/Users/ciel/Downloads/a b c.txt”};
Process process = Runtime.getRuntime().exec(cmdarray);
InputStream in = process.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
BufferedReader br = new BufferedReader(reader);
StringBuffer sb = new StringBuffer();
String message;
while ((message = br.readLine()) != null) {
sb.append(message);
}
System.out.println(sb);
// 得到文件的内容:
// 测试内容
```
]]>文章标题:Java 调用 CMD 命令时路径名带空格的解决方案
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2021/07/24/java-cmd/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
因为 coding.net 经常改版,导致我之前用 Coding Pages 进行的托管不知何时失效了,于是去官网看了下,意识到需要重新设置网站托管。(印象中,coding 最几年一直在做产品调整,每次点进它的网页,都不太一样)
首先是添加需要关联的域名,添加以后证书状态会显示审核中,点击【审核中】,跳转到腾讯云进行域名验证。关于这一步的操作我就不赘述了,大概流程就是需要在域名解析增加一条规定的记录来证明该域名确实是你自己的。
审核完以后,你就可以把 Coding 告诉你的 CNAME 指向,添加到域名解析中。
当我以为大功告成的时候,却出现了一个问题。此时我访问 cielni.com 是ok的,但访问 www.cielni.com 的时候,却显示The requested URL ‘/‘ was not found on this server。可我明明配了www的主机记录呀。
经过一番查阅后,在知乎上看了了一个比较靠谱的答案:
requested URL ‘/‘ was not found on this server. ? - 极客兔子的回答 - 知乎
引用一下里面的结论就是
使用什么域名接入腾讯云CDN服务,那就应该使用正确的域名做CNAME解析,例如以下示例。
错误的CNAME解析:
blog.douniwan.club -> douniwan.club.cdn.dnsv1.com
www.douniwan.cluub -> douniwan.club.cdn.dnsv1.com
正确的CNAME解析:
douniwan.club -> douniwan.club.cdn.dnsv1.com
www.douniwan.cluub -> www.douniwan.club.cdn.dnsv1.com
也就是说,在使用腾讯云的 CNAME 指向时,www 类型的主机记录,相应的就需要 www 开头的 CNAME值。
于是我修改了我的域名解析设置:
但是,我等了十分钟解析生效以后,问题还是存在。
我继续看了下那个知乎的提问,注意到了腾讯云的官方回答:
requested URL ‘/‘ was not found on this server. ? - 腾讯云小助手的回答 - 知乎
加速域名是精准匹配的,www. ruankun.xyz 如果需要访问加速,需要单独在CDN 接入,获取新的CNAME,配置即可。
虽然他说的是 CDN 的问题,但我瞬间明白了,我并没有想腾讯云申请 www 开头的 CNAME 指向,所以我在域名解析添加的 www.cielni.com.cdn.dnsv1.com 其实是不存在的。往前溯源,其实是因为我在 Coding 只绑定了 cielni.com 这个域名,而没有绑定 www.cielni.com。于是我果断去 Coding 增加了域名绑定,果然得到了 www.cielni.com.cdn.dnsv1.com 这个 CNAME 指向。
重新部署以后访问 www.cielni.com 成功!
Case closed.
]]>文章标题:使用 Coding 进行网站托管时的域名解析采坑之旅
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2021/07/04/coding-deposit/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
|
|
人脸识别使用的是Github上的一个人脸识别库 face-api.js 。face-api.js可以识别出视频流中人脸的轮廓和表情,调整识别精度等,Github上有详细的使用教程。
|
|
实现实时跟踪的思路就是,通过定时器,每隔1秒钟对当前的图像进行人脸识别并描边,这样间接实现了对视频的实时人脸跟踪,如果想要跟踪速度更加灵敏一点,可以把时间间隔改成0.1秒。
|
|
最后,程序会每分钟发送一次识别结果到服务器,服务器最终会计算每个人在一天内第一次的识别时间和最后一次识别时间作为上下班打卡时间,然后计算一天内识别到人脸的比例,可以作为在岗率的参考。
]]>文章标题:基于face-api.js的人脸实时跟踪
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2020/03/07/brower-face-detect/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
在跨年期间,如果在日期格式化的时候使用 YYYY 来格式化年份,则可能会出现下图所示的bug:
YYYY 在官方文档中的解释是 week-based-year,表示当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,那么这周就算入下一年。所以2019年12月31日那天在这种表述方式下就已经是 2020 年了。而当使用 yyyy 或者 uuuu 的时候,就还是 2019 年。
|
|
u 与 y 在公元后的年份表示没有区别,在公元前的年份表示有正负号的差别。所以建议平时时期格式化的时候使用 yyyy-MM-dd 或者 uuuu-MM-dd。
]]>文章标题:Java 中 YYYY-MM-dd 在跨年时的致命问题
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2020/01/10/java-date-format/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
有一张数据库表,记录了一些抖音视频每小时的播放量与点赞量,每个视频每小时都会产生一条记录。现在需要查出每个视频最新的一条记录,希望通过一条sql语句搞定。第一反应就是对 video_id 进行 Group By ,然后想办法取出每一组中 created_time 最新的那条数据。在最后加 Order By 显然是行不通的,因为 Order By 是对 Group By 的结果进行排序。
关于这种问题,网上有很多错误的解决方法,思路是先通过一个子查询把数据按照 created_time 倒序排序,然后再进行 Group By,sql语句如下:
|
|
这个方法的成立需要一个前提,就是MySQL 在 Group By 后是按照当前数据排列顺序选择第一条记录的。
然而我查阅了MySQL 5.7 版本的官方文档
If ONLY_FULL_GROUP_BY is disabled, a MySQL extension to the standard SQL use of GROUP BY permits the select list, HAVING condition, or ORDER BY list to refer to nonaggregated columns even if the columns are not functionally dependent on GROUP BY columns. This causes MySQL to accept the preceding query. In this case, the server is free to choose any value from each group, so unless they are the same, the values chosen are nondeterministic, which is probably not what you want. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause.
这段话总结一下就是, 在 ONLY_FULL_GROUP_BY 这个配置关闭的情况下,MySQL 从 Group 中选择记录的方式是随意的,无论预先对源数据如何进行 Order By,都不会对选择有任何影响。
ONLY_FULL_GROUP_BY 这个配置决定了能否在 SELECT 后的字段中出现 Group By 后没有的字段。ONLY_FULL_GROUP_BY 为 disabled时,允许SELECT 后的字段中出现 Group By 后没有的字段;ONLY_FULL_GROUP_BY 为 enable 时, 只能 SELECT 在 Group By 后出现的字段。而在大多数情况下,为了减轻程序员编写sql语句的压力,ONLY_FULL_GROUP_BY 都会建议设为 disabled。
我的数据库 ONLY_FULL_GROUP_BY 是设为 disabled的,我跑了上述方法,事实证明这个方法确实是无效的,无论我预先如何排序,从 Group 中选择出来的记录都是 id 最小的那一条。但是我也不能说 Group By 以后就是选择 id 最小的那一条,因为文档中明确说明了是 free to choose,我们无法去做其他猜测。也许在有自增主键的情况下,是取主键最小的那一条,但是这个目前无法百分之百证实。
因为在这个表中 created_time 最新也就意味着 id 最大,所以我变通一下,把问题简化为取每个视频中 id 最大的一条记录。这个问题可以通过聚合函数 MAX 先把每个视频的最大 id 查出来,然后在对这些 id 查询记录。sql语句如下:
在数据库 ONLY_FULL_GROUP_BY 是 disabled 的情况下:
|
|
随机选择一条
|
|
随机选择一条,而且子查询里面的 ORDER BY 会被优化掉。
]]>文章标题:MySQL 中 Group By 后如何选择记录
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2019/08/17/mysql-group-by-select/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
一般情况下,一百万以下数据量的表可以直接进行字段添加操作。而如果数据表的大小达到几百万几千万甚至更多时,添加一个字段会引起数据库卡死。经查阅,数据库大表添加字段有以下两个方法。
创建一个新表,复制旧表的结构(包含索引)
给新表加上添加需要新增的字段
把旧表的数据复制到新表,注意需要分批循环插入,不然容易卡死
删除旧表,重命名新表的名字为旧表的名字
使用 Percona 的在线工具在线修改表结构
]]>
|
|
|
|
把 java 项目打包成 jar 包时,资源文件夹 resources 下的文件会被打包进 jar 包里面。当使用 jar 运行整个项目时, 通过 getResource() 方法获得的资源文件路径会变成 xxx.jar!/xxx 的格式,这种格式的路径,不能通过 new File 的方式找到文件。这里我提供两个思路解决这个问题。
在这种情况下,如果想让 jar 读取到自己的资源文件,可以通过类加载器的 getResourceAsStream() 方法来解决。
既然无法读取 jar 包内部的文件,那可以转变思路,使资源文件出现在 jar 包的同级目录下,然后可以获取 jar 包的路径,从而获得资源文件的路径。
由于我的项目是多模块,所以我把资源文件放在项目根目录下,同主模块处在同级。这样打包以后,资源文件不会被打包进 jar 包内,而是和主模块同级。然后通过 Docker 把资源文件移动到 jar 包所在的目录下,这样就可以轻松访问到资源文件了。
Docker 指令如下:
|
|
需要注意的是,上述方法只适用于 Docker 环境下,所以我们需要区分项目是在 IDE 环境运行,还是 Docker 环境下使用 jar 包运行。可以通过以下代码来判断,同时获取 jar 包的所在目录
|
|
]]>文章标题:使用 jar 包运行项目时的资源文件定位问题
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2018/12/15/docker-jar-file/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
BI系统需要查询北京时间的某一天内公司所有设备激活记录,而数据库中记录的激活时间都是0时区的,系统需要不管在本地(GMT+8)还是服务器(GMT+0)上,都能准确查询导数据。
首先我们需要得到北京时间某一天的起始时间和结束时间的时间戳:
|
|
然后使用该时间戳区间编写mysql语句,值得注意的是,因为程序会自动把时间戳转化成字符串格式的时期去数据库进行比较,而这个转换是根据系统时区进行的,也就是说在本地和在服务器,转换出来的时间会差8个小时。所以需要根据当前系统的时区,对时间戳作调整:
|
|
|
|
]]>文章标题:数据库查询的时区问题
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2018/07/14/jfinal-mysql-timezone/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
Stream 作为 Java 8 的一大亮点,好比一个高级的迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。
我们可以利用stream对数据进行分组并求和。示例如下:
|
|
输出如下:
|
|
在实际需求中,我们可能需要对一组对象进行分组,而且分组的条件有多个。比如对国家和产品类型进行双重分组,一种比较复杂的方法是先对产品类型进行分组,然后对每一个产品类型中的国际名进行分组求和。示例如下:
|
|
上面的方法在应对两个字段的分组要求时,还能应付的过来,但如果字段超过两个时,每增加一个字段,就会多出很多代码行,显然不太合理。更合理的方法是,增加一个 getKey()方法,返回多个字段组成的唯一key,比如通过下划线连接等等。示例如下:
|
|
]]>文章标题:Java8 stream 中利用 groupingBy 进行多字段分组求和
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2018/07/14/java-stream-groupingby/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
find_all( name , attrs , recursive , text , **kwargs )
find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。具体请看官方文档
其中,对于text参数的介绍如下:
通过 text 参数可以搜搜文档中的字符串内容和tag。与 name 参数的可选值一样, text 参数接受 字符串 、 正则表达式 、 列表、 True 。 看例子:
|
|
注意:如果使用 find_all 方法时同时传入了 text 参数 和 name 参数 。Beautiful Soup会搜索指定name的tag,并且这个tag的 tag.string 属性包含text参数的内容。结果中不会包含字符串本身。
上面提到,text参数相当于搜索 tag 的 tag.string , 而 tag.string 的规则如下:
如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点
如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同
如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None
所以当某个tag有多个子tag时,我们是无法通过text参数搜索到该 tag 的。比如下面这种情况:
|
|
我们无法通过 soup.find('a', text = 'abc')
来搜索该 a 标签。
这里我想到了一种方法,就是先把 a 标签中字符串之外的子元素删除:
|
|
使要搜索的tag变成如下形式:
|
|
这样就可以通过 soup.find('a', text = 'abc')
来搜索该 a 标签。
另外,除了标签中带有别的标签,还会有换行符和注释等等,这些的存在都会导致该标签无法通过text参数来搜索到:
|
|
换行符建议在 bs解析html文本之前,用replace()
方法去掉:
|
|
因为bs对换行符的解析会有一点点迷。
而注释的删除比较特别:
|
|
]]>文章标题:Beautiful Soup 文档搜索方法(find_all find)中 text 参数的局限与解决方法
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2018/06/28/beautifulsoup-note/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
Beautiful Soup是一个Python库,用来解析html和xml结构的文档。具体关于Beautiful Soup的介绍与使用,可以参考以下资料:
官方对各个解析器的比较如下:
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
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格式的文档 | 速度慢 不依赖外部扩展 |
首先,如果是解析从网页上爬下来的HTML文档,请不要使用lxml XML 解析器,因为HTML解析器和XML解析器对于一文档的解析方式是不同的。比如对于空标签<b />,因为空标签<b />不符合HTML标准,所以解析器把它解析成<b></b>,而使用XML解析时,空标签<b/>依然被保留。
其次,在Python2.7.3之前的版本和Python3中3.2.2之前的版本中,标准库中内置的HTML解析方法不够稳定,所以我推荐使用lxml或者html5lib作为html文档的解析器,因为容错性比较好。
在HTML或XML文档格式正确的情况下,不同解析器的差别只是解析速度的差别。而在很多情况下,我们从网页上爬取的HTML文档会有格式不严谨的地方,那么在不同的解析器中返回的结果可能是不一样的,此时用户需要选择合适的解析器来满足自己的需求。
|
|
从上面的例子可以看出,html5lib对于文档的容错性是最好的,它能补全大多数的标签。而lxml和python内置解析器会忽略结束标签,补全开始标签。
而对于部分没有结束标签的标签比如<input/>
、<img/>
等,在正常情况下,解析器都会正确解析,但如果是漏掉’/‘的情况下,例如<input><a></a>
:
|
|
可见,Python内置库解析无法正确补全不需要结束标签的标签,比如<input>
。
在find_all()方法中,如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索。如传入 href 参数,Beautiful Soup会搜索每个tag的”href”属性:
|
|
假如我们想用 class 过滤,不过 class 是 python 的关键词,这怎么办?加个下划线就可以
|
|
但有些tag属性在搜索不能使用,比如HTML5中的 data-*
属性,同时name由于已经是find_all()
方法中的一个参数名(代表tag的名字),所以也不可通过tag中的name属性来搜索tag,但是可以通过 find_all()
方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag,例如:
|
|
在Beautiful Soup,有两种获取标签内容的方法:.string属性 和 get_text()方法。
.string 用来获取标签的内容 ,返回一个 NavigableString 对象。
如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点。
如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同。
如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None。
get_text() 用来获取标签中所有字符串包括子标签的内容,返回的是 unicode 类型的字符串
实际场景中我们一般使用 get_text 方法获取标签中的内容。
在文档树中,使用 .next_sibling 和 .previous_sibling 属性来查询兄弟节点。实际文档中的tag的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白。例如:
|
|
如果以为第一个标签的 .next_sibling 结果是第二个标签,那就错了,真实结果是第一个标签和第二个标签之间的顿号和换行符:
|
|
所以我建议使用 find_next_sibling() 方法来查询兄弟节点:
|
|
在HTML文档中经常会出现一些用来换行<br>
标签,比如:
|
|
Beautiful Soup会将其自动补全为以下错误的形式:
|
|
因为<br>
标签是为了展示的美观而出现的,而我们在解析文档时,这种标签的出现会影响我们解析的正确性(就如上面那个例子所示)。为了解决这个问题,我们需要使用extract()方法将文档中的<br>
标签删掉
|
|
这样最终的文档格式就变为:
|
|
因为HTML标签是大小写敏感的,所以3种解析器再出来文档时都将tag和属性转换成小写。例如文档中的
]]>文章标题:Beautiful Soup 采坑之旅
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2018/06/14/beautifulsoup/
有问题或建议欢迎与我联系讨论,转载或引用希望标明出处,感激不尽!
我突然意识到给聊天记录截图这个习惯的意义了,如果把好友删了,聊天记录就没有了,但截图是不会丢的。也许我早就清楚自己是个丢三落四的人,甚至会把朋友弄丢,所以才有了这个截图的习惯吧。
不知道这算不算一种天分,我很容易把朋友弄丢,哪怕是那种一起玩了十年的朋友,就因为一点点小情绪,我都会毫不犹豫地拉黑,并且可以很坚定地在心中就此消除这个人,就像执行一行代码一样干净利落。现在回想起来,根本原因就是我从来没有珍惜过他。从初中以来,他给我送过很多礼物,可我从来没有给他送过什么。虽然假期经常一起玩,平时也经常聊天,但我从来没有真正珍惜过,不知道为何,大概我就是一个从不珍惜友情的人。
我是一个从不珍惜友情的人,也许是因为我很容易得到友情。可能是我性格温顺,为人很随和,无论我走到哪,好像都能结实一些很要好的朋友。在我的潜意识中,朋友只不过是在某一阶段陪你一起走的人罢了。所以我很少会从内心真正地感激过哪个朋友,真正地在乎过谁。
亦或许因为我喜欢一个人玩,喜欢待在自己的世界,从来不会深刻明白朋友的意义,甚至我有时还很享受和他人断绝关系后的如释重负。
我现在也有不少朋友,也有谈心的人,也有每天一起玩游戏的人,也有天天吹牛皮的人,也有在网络上互动的网友。只不过以前和我一起玩游戏的,一起谈心的人好像真的不见了。我也不知道现在的那些人在几年以后还会不会继续出现在我生命中,还是会突然地消失,就好像灭霸打了个响指。
可能对我而言,人生各个阶段做的事情都是类似的,只不过是身边人换了一批又一批而已。
其实我并没有想念他们,只不过感慨他们的消失。
毕竟啊,我是一个从不珍惜友情的人。
]]>之前写过一个简单的爬虫,每天获取公司insgtagram主页的粉丝数用来进行粉丝趋势的展示。代码很简单就是通过获取主页源代码后用正则表达式匹配其中的一串json数据,再用python的json解析库进行解析,从中获取粉丝数。
然而昨天这个爬虫报错了,我重新看了一下ins主页的网页源代码,发现其中增加了一段内容,这段内容正好被我匹配进去了。经过思考显而易见,这是贪婪匹配的问题。
现在这些术语听起来都很吓人,其实这是正则匹配的两种模式,很容易解释:
举个例子:aa<div>test1</div>bb<div>test2</div>cc
如果想要匹配一个完整的div,贪婪模式的结果为:<div>test1</div>bb<div>test2</div>
非贪婪模式的结果为:<div>test1</div>
可以发现贪婪模式会匹配尽可能长的字符串,而非贪婪模式在第一次匹配成功后就会停止匹配。
默认情况下匹配都是贪婪模式,如果要改成非贪婪模式,只需要量词后面加上一个问号?。
常用的量词有:
{m,n}
{m,}
?
*
+
这些默认都是贪婪模式,若改成非贪婪模式,只需这样:
{m,n}?
{m,}?
??
*?
+?
针对上面那个div的例子,贪婪模式的匹配表达式为:<div>.*</div>
非贪婪模式的匹配表达式为:<div>.*?</div>
贪婪模式就是匹配最长的字符串,非贪婪模式就是匹配最短字符串。
默认情况下匹配都是贪婪模式,如果要改成非贪婪模式,只需要量词后面加上一个问号?。
使用贪婪模式还是非贪婪模式,这主要取决于我们的需求。但有一点,非贪婪模式的性能一定是高于贪婪模式的。
最后,附上我的爬虫代码:
|
|
]]>文章标题:正则表达式的贪婪匹配与非贪婪匹配
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2017/09/07/greedy-matching/
有问题或建议欢迎在我的博客讨论,转载或引用希望标明出处,感激不尽!
站在毕业的路口,有人说舍不得那些经历过的人和事,有人说只是偶尔怀念过去的种种。有人因为离别痛哭流涕,有人因为未来而理性分析过去的得与失。而我一定属于前者,从小感情泛滥超过大部分女孩,看周冬雨的《喜欢你》会感动哭,更别说《从你的全世界路过》。
那天看到同学群里有人发了一张初三时的照片,然后平日里冷清的群开始了吐槽。大家默默找到自己,然后感叹一句,那时候的自己好傻。我在回忆自己十年之前经历过的事和认识的人,十年之后,那些夕阳下的伙伴,那些旧日的足迹,还剩多少余温。
前些天和朋友聊起《我的前半生》里面的贺涵,对于贺涵和唐晶之间十年的感情,似有惋惜又在情理之中。朋友说,当他和唐晶冲突变多,当他心甘情愿地为子君付出的时候,也许贺涵已经找到了更合适的人。是啊,十年的时间,唐晶已经不再是那个初出茅庐的实习生,我爱你是因为我爱当初的那个你,谁能保证十年的交往之后还深爱出当初的感觉。在我看来很多时候,感情都没有对错,大概一切源于人性吧。
后来我总算学会了如何去爱
可惜你早已远去消失在人海
后来终于在眼泪中明白
有些人一旦错过就不再
我在想,如果有一个人,认识十年后还觉得她是当初的模样,那就爱吧。
Allen Yin
二零一七年八月十五日零点五十三分
]]>文章作者: Allen Yin
微信: cherisherwindy
邮箱: sjtuyinlei@163.com
当初选择软件专业我是没有太多犹豫的,不是说我对软件这个行业有多感兴趣,而是把不感兴趣和我认为没有前途的专业排除掉后只剩下它了。当时我觉得兴趣什么的并不重要,就业压力摆在那自己也会好好学的。
经过大概一个多学期,我就知道我错了。我们可以假设高中的学习是一个维度的,努力学习成绩会上来一点,松懈了会下降一点,我们只在乎好好学习这件事,似乎这就是评判一个高中生的唯一标准。到了大学,你会发现就算你认真完成了老师布置的作业,好好复习认真备考,还是达不到预期。因为大学的学习不止这一个维度,那些对本专业具有浓厚兴趣的学生往往会自主去探索远超于课本的内容,在专业方面远远超过了只是认真完成老师任务的学生。有时候觉得自己很努力地在学专业知识了,很努力在在写代码,尝试超越书本了,却还是力不从心。因为别人先天的兴趣优势,而我说实在的对写代码又有多少兴趣呢?
我在富士通面试的时候,在我前一个面试的学生是一个拥有大量项目经验的软院同学,尽管看成绩的话我似乎稍好一点,但我看的出面试官心里是更倾向他的。在面试的过程中他不断地问我如何看待别人拥有如此多的项目经历而你却没有什么。我也无言以对。大学期间自主参加的项目确实偏少,很大的原因就是对这些软件比赛提不起兴趣,没有什么热情。
这可能就是一个先天的优势,如果大学是一场400米比赛,那么在我看来拥有专业兴趣的人在起跑的时候就已经甩开我100多米了。
在经历了大学四年后,我更加确信了兴趣真的是最好的老师。如果有你非常感兴趣的专业,请一定不要犹豫,你不知道那些选择自己不感兴趣的专业的学长学姐们学得有多痛苦。
《火星救援》的结尾,马特达蒙有一段台词:
At some point, everything’s going to go south on you.
在某个时候,一切都会变得越来越糟糕Everything’s going to go south and you’re going to say “This is it.”
当一切越来越糟时,你只能坚强地面对This is how I end.
这是我如何解决这个问题的Now you can either accept that
你要么屈服or you can get to work.
要么反抗That’s all it is.
就是这样You just begin.
你只要开始You do the math. You solve one problem
进行计算,解决一个问题and you solve the next one,
解决下一个问题and then the next.
解决下下个问题And if you solve enough problems, you get to come home.
等解决了足够的问题,你就能回家了
在高中的学习生活中,我们遇到的无非是一道难题之类的颗粒度较小的问题。而到了大学迎面袭来的可不是一道题目这样可控的东西了,可能是一个需要从头开始组织的活动,可能是一场毫无准备的考试,可能是一项毫无头绪的课程设计。每当我知道自己要面对这类的问题时,我总会出现短暂的拖延症状。可能是不知道如何开始,也可能是害怕开始,因为一旦开始,就要连续好几天没日没夜地投身进去。我本能地会去想,能不能不要做。这种想法可以让我逃掉软件比赛的压力,却逃不掉考试,逃不掉实习。
唯一的办法就是快速制定计划,然后开始执行计划。就像马特达蒙说的 “You solve one problem and you solve the next one, and then the next. And if you solve enough problems, you get to come home.” 把未来的任务分成一个个颗粒足够细的小任务,然后一个接着一个去解决这些任务,等到把所有的任务都解决了,你的工作差不多就完成了。
空想计划没有用,一定要去做。我现在觉得我大学四分之一的时间浪费在了等待与空想上。
这是我大学时期体会最最最深刻的一句话,大学里大大小小很多事情基本都在我心里印证了它。
刚进大学那会儿,我们对学长学姐是很依赖的,我们总想从学长学姐口中得到关于大学许许多多的经验,好让自己少走一点弯路,在本科的道路上走得更轻盈一点。
然而问题是,经验实际上是一个很虚的东西,它是因人而异的。在大多数情况下,学长学姐们的经验是无法取代自己的亲身经历的。
首先,学长学姐把他们通过亲身经历得来的经验用语言表达出来的时候,大多数都是笼统的概括,给我们一个大概的印象。比如这门课坑不坑,考试难不难,这个老师好不好,这个知识点重不重要等等。但是课程难在哪里,考试重点是什么等等,这些细节方面是他们无法告诉我们却又是很重要的。只有我们真真切切经历过,我们才会形成一个完整的认知,到时候你会发现,这根本不是难不难和坑不坑所能概括的。从学长和学姐的视角来说,他们无法把完整的认知传达给我们,只能用通俗简易的形容词把他们的印象最直接地传达给我们。
其次。经验什么的都是因人而异的,具有特殊性和偶然性。仅靠前人的判断下定结论显然太过随便,最多只能当参考。
另外,很多感受是要经历过才能更深刻的,在大学四年这个成长阶段,有些该踩的坑是一定要踩的,有些该碰的壁是一定要碰的,这样在以后的经历中才能避免重复犯错。这和解一道数学题目是一样的,看一遍参考答案和自己完完整整的做一遍的效果是天差地别的。
]]>由于安卓手机的配置不尽相同,在公司推出安卓版360度全景相机 Insta360 Air 后,客服经常会收到来电,询问其手机型号是否适用该产品。大部分情况下顾客只知道自己的手机型号,却不知道其详细参数,这让客服的工作量大大增加。于是客服主管希望能把ZOL 中关村在线里的所有安卓系统的手机型号和其对应的参数通过爬虫搜集下来,做成Excel表格方便以后随时随地检索手机参数。
在选择限定的操作系统条件后,得到该url,经过测试发现,url最后下划线后面的数为页码。不过,手机列表的参数信息是不完整的,点击更多参数可以得到每个手机型号的详细参数信息,所以我们应该存下每个手机型号更多参数页面的url。
针对每一个型号的手机,访问其参数详情页进行参数采集。
关于如何选用何种方式进行爬虫采集。由于ZOL中关村在线的手机信息数据都是在请求url时就同步返回给浏览器的,不存在js异步加载的问题,所以我们可以直接用urllib2库或者requests来请求url获取网页信息。由于网页信息比较复杂,我们需要 Beautiful Soup 来帮助我们解析html页面,获取参数信息。(Beautiful Soup教程)
下面通过代码加注释来介绍具体的操作步骤,在这之前希望大家已经看过上面的Beautiful Soup教程,对Beautiful Soup的使用方法有一定了解。
|
|
Github地址: https://github.com/NiShuang/mobile_info_crawler
导出的Excel表格如下图所示:
]]>文章标题:ZOL中关村在线手机参数爬虫
文章作者:Ciel Ni
文章链接:http://www.cielni.com/2017/07/28/zol-phone-crawler/
有问题或建议欢迎在我的博客讨论,转载或引用希望标明出处,感激不尽!