go-15.png
15.png
大家好,我叫谢伟,是一名程序员。
在未来人人都是手艺人。
传统的手艺人在圈子内创造影响力,在互联网时代,个人影响力不仅仅限于圈子内,互联网创造无限可能性。
好,今天的主题:作品意识
1、作品意识
作品很好理解,比如歌手发行唱片、发行单曲,作家写书等,前端工程师可能更容易出作品,比如,写一个优雅的网站,比如写一个优雅的工具,写一个实用的小程序,开发一个个人的APP等,这些都是作品。
后端人员,可以写库,虽然在和真实用户交互层面,后端工程师开发的工具大概只能在程序员内使用,或者有一定编程基础的人才能使用。
尽管不是每个人写的工具都能得到广泛的认可、使用。作为程序员,一定要有作品意识,什么意思?
写用户交互友好的工具
写符合人性的工具
写简洁的工具
对于后端开发者来说最终体现出来的作品便是“不断的创造轮子、或者重复造轮子”。
关于是否重复造轮子,知乎有广泛的讨论,我的观点是:想要提高个人的技能水平,需要不断的造轮子,水平低,造简单的轮子,水平渐渐提升,造更高级点的轮子。在过程参考优秀开源工具的实现、思想。
模仿是最简单的学习方式
2、如何产出作品
在工作之余,我较长时间放在 Github 上。去发现一些好的项目,去参考一些好的效果。当然作为后端开发者,我不太会去看前端的项目,同样,作为Gopher ,我倾向于 关注 Go 项目,但如果是火热的 Python 项目,那我也会关注下。
随着关注点的越来越精细,我倾向于从我熟悉的东西入手,什么意思,为什么从熟悉的东西入手,因为我越来越发现,自信心是很重要的,如果你不能第一时间对一个项目提起兴趣和自信心,你可能没什么机会和这个项目产生化学反应。
近期在阅读 requests-html。
我比较熟悉爬虫
这是一个网页信息解析的库
代码量不是很大,阅读起来简便
import sysimport asynciofrom urllib.parse import urlparse, urlunparse, urljoinfrom concurrent.futures import ThreadPoolExecutorfrom concurrent.futures._base import TimeoutErrorfrom functools import partialfrom typing import Set, Union, List, MutableMapping, Optionalimport pyppeteerimport requestsfrom pyquery import PyQueryfrom fake_useragent import UserAgentfrom lxml.html.clean import Cleanerimport lxmlfrom lxml import etreefrom lxml.html import HtmlElementfrom lxml.html import tostring as lxml_html_tostringfrom lxml.html.soupparser import fromstring as soup_parsefrom parse import search as parse_searchfrom parse import findall, Resultfrom w3lib.encoding import html_to_unicode
从导入的库来看,这个网页解析的库主要是整合了各种第三方库和内置库,这意味着,很少有直接从零开始造轮子,可以借助已经比较优秀的第三方库。
好,关于这个库的分析,我下次讲。
取其中的一个库来分析:fake_useragent
这是一个生成UserAgent 的库,可以指定浏览器的类型,也可以随机生成UserAgent。
我一不小心对它产生了兴趣。
网上一般的讲解如何随机生存UserAgent 的处理方法是,在本地缓存一个大的文件,随机从文件内取一个。当然这看上去不够极客唉。
1、使用
from fake_useragent import UserAgent ua = UserAgent() ua.ie# Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US);ua.msie# Mozilla/5.0 (compatible; MSIE 10.0; Macintosh; Intel Mac OS X 10_7_3; Trident/6.0)'ua['Internet Explorer']# Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .NET CLR 3.3.69573; WOW64; en-US)ua.opera# Opera/9.80 (X11; Linux i686; U; ru) Presto/2.8.131 Version/11.11ua.chrome# Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2'ua.google# Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/537.13 (KHTML, like Gecko) Chrome/24.0.1290.1 Safari/537.13ua['google chrome']# Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11ua.firefox# Mozilla/5.0 (Windows NT 6.2; Win64; x64; rv:16.0.1) Gecko/20121011 Firefox/16.0.1ua.ff# Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:15.0) Gecko/20100101 Firefox/15.0.1ua.safari# Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25# and the best one, random via real world browser usage statisticua.random
用法十分简单,毕竟完成的功能就不是很复杂。
随机生成
指定浏览器生成
2、阅读 fake_useragent 文档
grabs up to date useragent from useragentstring.com
randomize with real world statistic via w3schools.com
可以看出,其实是从网上抓取到的信息,在随机生成的。
http://useragentstring.com/pages/useragentstring.php?name=Chrome
http://useragentstring.com/pages/useragentstring.php?name=Safari
http://useragentstring.com/pages/useragentstring.php?name=Firefox
http://useragentstring.com/pages/useragentstring.php?name=Opera
http://useragentstring.com/pages/useragentstring.php?name=Internet+Explorer
那作者如何选择浏览器?只选择这几个浏览器的理由是?
image.png
可以查看当前最主流的浏览器的一些数据。
由于一些大家都知道的原因,这个网站的访问需要借助一些手段。
Sometimes, useragentstring.com or w3schools.com changes their html, or down, in such case fake-useragent uses CDN cloudfront fallback
意味着还可以从这个网站上获取UserAgent
3、梳理
这是一个获取 UserAgent 的库
主要的数据来自两个网站
根据统计数据得出主流的浏览器
本质是一个爬虫
4、源代码
def load(self): try: with self.load.lock: if self.cache: self.data = load_cached( self.path, use_cache_server=self.use_cache_server, verify_ssl=self.verify_ssl, ) else: self.data = load( use_cache_server=self.use_cache_server, verify_ssl=self.verify_ssl, ) # TODO: change source file format # version 0.1.4+ migration tool self.data_randomize = list(self.data['randomize'].values()) self.data_browsers = self.data['browsers'] except FakeUserAgentError: if self.fallback is None: raise else: logger.warning( 'Error occurred during fetching data, ' 'but was suppressed with fallback.', )
def __getattr__(self, attr): if attr in self.safe_attrs: return super(UserAgent, self).__getattr__(attr) try: for value, replacement in settings.REPLACEMENTS.items(): attr = attr.replace(value, replacement) attr = attr.lower() if attr == 'random': browser = random.choice(self.data_randomize) else: browser = settings.SHORTCUTS.get(attr, attr) return random.choice(self.data_browsers[browser]) except (KeyError, IndexError): if self.fallback is None: raise FakeUserAgentError('Error occurred during getting browser') # noqa else: logger.warning( 'Error occurred during getting browser, ' 'but was suppressed with fallback.', ) return self.fallback
核心代码是这两个函数。
数据来源:
CACHE_SERVER = 'http://d2g6u4gh6d9rq0.cloudfront.net/browsers/fake_useragent_{version}.json'.format( # noqa version=__version__, ) BROWSERS_STATS_PAGE = 'https://www.w3schools.com/browsers/default.asp'BROWSER_BASE_PAGE = 'http://useragentstring.com/pages/useragentstring.php?name={browser}' # noqa
5、实现
你已经知道了这是个Python 库。好了,你也了解了这个库的核心代码和思想。
你下一步怎么做?
重新实现。
你可以选择 Python 实现,但是在你看源代码的过程中,你的思维应该已经受这个库的具体处理方式影响了。
所以,你可以用其他语言进行处理,核心思想还在,但规则变了。所以你会花费一点点的思考。这样其实对你自己有好处。
5.1、项目组织
按照领域驱动的思想,将项目区分为四层:UI、Infra、Domain、Application
├─application ├─domain │ ├─global│ └─parse ├─infra │ └─download ├─main ├─ui
infra层:
package download import ( "net/http" "errors" "github.com/PuerkitoBio/goquery") var ( ErrRequest = errors.New("request err") ErrResponse = errors.New("response err") ) func ResponseDownload(url string) (*goquery.Document, error) { request, err := http.NewRequest("GET", url, nil) if err != nil { return nil, ErrRequest } request.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36") client := http.DefaultClient response, err := client.Do(request) if err != nil { return nil, ErrResponse } defer response.Body.Close() return goquery.NewDocumentFromReader(response.Body) }
本质是一个爬虫,获取网页信息少不了这个函数。
domain 层:
package globalimport "fmt"const Version = "0.1.10"var ( BROWSERS_STATS_PAGE = "https://www.w3schools.com/browsers/default.asp" BROWSER_BASE_PAGE = "http://useragentstring.com/pages/useragentstring.php?name=%s" CACHE_SERVER = "http://d2g6u4gh6d9rq0.cloudfront.net/browsers/fake_useragent_%s.json") var OVERRIDES = make(map[string]string) var LOCALUSERAGENT = make(map[string][]string) func init() { OVERRIDES = map[string]string{ "Edge/IE": "Internet Explorer", "IE/Edge": "Internet Explorer", } CACHE_SERVER = fmt.Sprintf(CACHE_SERVER, Version) }
全局参数,主要是上面提到的网站信息。
BROWSERS_STATS_PAGE
BROWSER_BASE_PAGE
CACHE_SERVER
package parse import ( "errors" "github.com/PuerkitoBio/goquery" "github.com/tidwall/gjson")var ( ErrArray = errors.New("array err") ErrInvalid = errors.New("invalid json") ) func CloudFront(doc *goquery.Document, browserType string) ([]gjson.Result, error) { if !gjson.Valid(doc.Text()) { return nil, ErrInvalid } jsonResult := gjson.Parse(doc.Text()) browserUserAgent := jsonResult.Get("browsers." + browserType) browserUserAgentOk := browserUserAgent.IsArray() if !browserUserAgentOk { return nil, ErrArray } return browserUserAgent.Array(), nil }
从 cloudfront 网站获取UserAgent
package parseimport ( "github.com/PuerkitoBio/goquery")var browserList = []string{ "Internet Explorer", "Opera", "Chrome", "Safari", "FireFox", }func UserAgentCom(doc *goquery.Document) ([]string, error) { var newBrowserList = make([]string, 1) doc.Find("div#liste ul li").Each(func(i int, selection *goquery.Selection) { userAgent := selection.Find("a").Text() //fmt.Println(userAgent) newBrowserList = append(newBrowserList, userAgent) }) return newBrowserList, nil }
从 useragentstring.com 网站获取 UserAgent
Application 层:
package applicationimport ( "errors" "fake-user-agent-go-ng/domain/global" "fake-user-agent-go-ng/infra/download" "fmt" "fake-user-agent-go-ng/domain/parse" "math/rand" "time" "github.com/PuerkitoBio/goquery" "github.com/tidwall/gjson")type FakeUserAgent struct { UserAgentStringOk bool CloudFrontNetOk bool Cache bool} var ( ErrUserAgent = errors.New("user agent err") )func NewFakeUserAgent(UserAgentStringOk bool, CloudFrontNetOk bool, CacheOK bool) *FakeUserAgent { return &FakeUserAgent{ UserAgentStringOk: UserAgentStringOk, CloudFrontNetOk: CloudFrontNetOk, Cache: CacheOK, } } func (F *FakeUserAgent) IE() string { return F.common("Internet+Explorer") } func (F *FakeUserAgent) InternetExplorer() string { return F.IE() } func (F *FakeUserAgent) Msie() string { return F.IE() } func (F *FakeUserAgent) Chrome() string { return F.common("Chrome") } func (F *FakeUserAgent) Google() string { return F.Chrome() } func (F *FakeUserAgent) Opera() string { return F.common("Opera") } func (F *FakeUserAgent) Safari() string { return F.common("Safari") } func (F *FakeUserAgent) FireFox() string { return F.common("Firefox") } func (F *FakeUserAgent) FF() string { return F.FireFox() } func (F *FakeUserAgent) Random() string { randomChoice := []string{ "Chrome", "Firefox", "Safari", "Opera", "Internet+Explorer", } r := rand.NewSource(time.Now().UnixNano()) random := rand.New(r) browserType := randomChoice[random.Intn(len(randomChoice))] return F.common(browserType) } func (F *FakeUserAgent) common(browserType string) string { r := rand.NewSource(time.Now().Unix()) randomChoice := rand.New(r) if F.Cache { index := randomChoice.Intn(len(global.LOCALUSERAGENT[browserType])) return global.LOCALUSERAGENT[browserType][index] } var url string if F.UserAgentStringOk { url = fmt.Sprintf(global.BROWSER_BASE_PAGE, browserType) } else { url = global.CACHE_SERVER } var ( doc *goquery.Document err error ) doc, err = download.ResponseDownload(url) if err != nil { fmt.Println(ErrUserAgent) panic(ErrUserAgent) } var ( userAgentList []string ) if F.UserAgentStringOk { userAgentList, err = parse.UserAgentCom(doc) if err != nil { fmt.Println(ErrUserAgent) panic(ErrUserAgent) } return userAgentList[randomChoice.Intn(len(userAgentList))] } if F.CloudFrontNetOk { var userAgentResult []gjson.Result userAgentResult, err = parse.CloudFront(doc, browserType) if err != nil { fmt.Println(ErrUserAgent) panic(ErrUserAgent) } return userAgentResult[randomChoice.Intn(len(userAgentResult))].String() } return ""}
主要是实现这几个函数:
Random 随机得到一个 UserAgent
IE/Msie/InternetExplorer 返回IE 浏览器UserAgent
FF/FireFox 返回 FireFox 浏览器UserAgent
Google/Chrome 返回 Chrome 浏览器UserAgent
Opera 返回 Opera 浏览器UserAgent
同时使用下面几个参数,决定从哪个网站抓取,还是使用本地缓存。
UserAgentStringOk: http://useragentstring.com/ 网站的UserAgent
CloudFrontNetOk: http://d2g6u4gh6d9rq0.cloudfront.net/browsers/fake_useragent_0.1.9.json 的UserAgent
CacheOk : 代码内置的随机 50 个UserAgent
6. 开源
[图片上传失败...(image-cb2b7f-1531152156667)]
欢迎使用唉。
本节完,再会,我是谢伟。
作者:谢小路
链接:https://www.jianshu.com/p/cba8f921d746
共同学习,写下你的评论
评论加载中...
作者其他优质文章