当前位置: 首页 > 编程日记 > 正文

Python 多线程抓取网页 牛人 use raw socket implement http request great

Python 多线程抓取网页 - 糖拌咸鱼 - 博客园

Python 多线程抓取网页

最近,一直在做网络爬虫相关的东西。 看了一下开源C++写的larbin爬虫,仔细阅读了里面的设计思想和一些关键技术的实现。

1、larbin的URL去重用的很高效的bloom filter算法;
2、DNS处理,使用的adns异步的开源组件;
3、对于url队列的处理,则是用部分缓存到内存,部分写入文件的策略。
4、larbin对文件的相关操作做了很多工作
5、在larbin里有连接池,通过创建套接字,向目标站点发送HTTP协议中GET方法,获取内容,再解析header之类的东西
6、大量描述字,通过poll方法进行I/O复用,很高效
7、larbin可配置性很强
8、作者所使用的大量数据结构都是自己从最底层写起的,基本没用STL之类的东西
......
还有很多,以后有时间在好好写篇文章,总结下。

这两天,用python写了个多线程下载页面的程序,对于I/O密集的应用而言,多线程显然是个很好的解决方案。刚刚写过的线程池,也正好可以利用上了。其实用python爬取页面非常简单,有个urllib2的模块,使用起来很方便,基本两三行代码就可以搞定。虽然使用第三方模块,可以很方便的解决问题,但是对个人的技术积累而言没有什么好处,因为关键的算法都是别人实现的,而不是你自己实现的,很多细节的东西,你根本就无法了解。 我们做技术的,不能一味的只是用别人写好的模块或是api,要自己动手实现,才能让自己学习得更多。

我决定从socket写起,也是去封装GET协议,解析header,而且还可以把DNS的解析过程单独处理,例如DNS缓存一下,所以这样自己写的话,可控性更强,更有利于扩展。对于timeout的处理,我用的全局的5秒钟的超时处理,对于重定位(301or302)的处理是,最多重定位3次,因为之前测试过程中,发现很多站点的重定位又定位到自己,这样就无限循环了,所以设置了上限。具体原理,比较简单,直接看代码就好了。

自己写完之后,与urllib2进行了下性能对比,自己写的效率还是比较高的,而且urllib2的错误率稍高一些,不知道为什么。网上有人说urllib2在多线程背景下有些小问题,具体我也不是特别清楚。

先贴代码:

fetchPage.py  使用Http协议的Get方法,进行页面下载,并存储为文件

?
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
'''
Created on 2012-3-13
Get Page using GET method
Default using HTTP Protocol , http port 80
@author: xiaojay
'''
import socket
import statistics
import datetime
import threading
socket.setdefaulttimeout(statistics.timeout)
class Error404(Exception):
    '''Can not find the page.'''
    pass
class ErrorOther(Exception):
    '''Some other exception'''
    def __init__(self,code):
        #print 'Code :',code
        pass
class ErrorTryTooManyTimes(Exception):
    '''try too many times'''
    pass
def downPage(hostname ,filename , trytimes=0):
    try :
        #To avoid too many tries .Try times can not be more than max_try_times
        if trytimes >= statistics.max_try_times :
            raise ErrorTryTooManyTimes
    except ErrorTryTooManyTimes :
        return statistics.RESULTTRYTOOMANY,hostname+filename
    try:
        s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        #DNS cache
        if statistics.DNSCache.has_key(hostname):
            addr = statistics.DNSCache[hostname]
        else:
            addr = socket.gethostbyname(hostname)
            statistics.DNSCache[hostname] = addr
        #connect to http server ,default port 80
        s.connect((addr,80))
        msg  = 'GET '+filename+' HTTP/1.0\r\n'
        msg += 'Host: '+hostname+'\r\n'
        msg += 'User-Agent:xiaojay\r\n\r\n'
        code = ''
        f = None
        s.sendall(msg)
        first = True
        while True:
            msg = s.recv(40960)
            if not len(msg):
                if f!=None:
                    f.flush()
                    f.close()
                break
            # Head information must be in the first recv buffer
            if first:
                first = False
                headpos = msg.index("\r\n\r\n")
                code,other = dealwithHead(msg[:headpos])
                if code=='200':
                    #statistics.fetched_url += 1
                    f = open('pages/'+str(abs(hash(hostname+filename))),'w')
                    f.writelines(msg[headpos+4:])
                elif code=='301' or code=='302':
                    #if code is 301 or 302 , try down again using redirect location
                    if other.startswith("http") :               
                        hname, fname = parse(other)
                        downPage(hname,fname,trytimes+1)#try again
                    else :
                        downPage(hostname,other,trytimes+1)
                elif code=='404':
                    raise Error404
                else :
                    raise ErrorOther(code)
            else:
                if f!=None :f.writelines(msg)
        s.shutdown(socket.SHUT_RDWR)
        s.close()
        return statistics.RESULTFETCHED,hostname+filename
    except Error404 :
        return statistics.RESULTCANNOTFIND,hostname+filename
    except ErrorOther:
        return statistics.RESULTOTHER,hostname+filename
    except socket.timeout:
        return statistics.RESULTTIMEOUT,hostname+filename
    except Exception, e:
        return statistics.RESULTOTHER,hostname+filename
def dealwithHead(head):
    '''deal with HTTP HEAD'''
    lines = head.splitlines()
    fstline = lines[0]
    code =fstline.split()[1]
    if code == '404' : return (code,None)
    if code == '200' : return (code,None)
    if code == '301' or code == '302' :
        for line in lines[1:]:
            p = line.index(':')
            key = line[:p]
            if key=='Location' :
                return (code,line[p+2:])
    return (code,None)
    
def parse(url):
    '''Parse a url to hostname+filename'''
    try:
        u = url.strip().strip('\n').strip('\r').strip('\t')
        if u.startswith('http://') :
            u = u[7:]
        elif u.startswith('https://'):
            u = u[8:]
        if u.find(':80')>0 :
            p = u.index(':80')
            p2 = p + 3
        else:
            if u.find('/')>0:
                p = u.index('/')
                p2 = p
            else:
                p = len(u)
                p2 = -1
        hostname = u[:p]
        if p2>0 :
            filename = u[p2:]
        else : filename = '/'
        return hostname, filename
    except Exception ,e:
        print "Parse wrong : " , url
        print e
def PrintDNSCache():
    '''print DNS dict'''
    n = 1
    for hostname in statistics.DNSCache.keys():
        print n,'\t',hostname, '\t',statistics.DNSCache[hostname]
        n+=1
def dealwithResult(res,url):
    '''Deal with the result of downPage'''
    statistics.total_url+=1
    if res==statistics.RESULTFETCHED :
        statistics.fetched_url+=1
        print statistics.total_url , '\t fetched :', url
    if res==statistics.RESULTCANNOTFIND :
        statistics.failed_url+=1
        print "Error 404 at : ", url
    if res==statistics.RESULTOTHER :
        statistics.other_url +=1
        print "Error Undefined at : ", url
    if res==statistics.RESULTTIMEOUT :
        statistics.timeout_url +=1
        print "Timeout ",url
    if res==statistics.RESULTTRYTOOMANY:
        statistics.trytoomany_url+=1
        print e ,"Try too many times at", url
if __name__=='__main__':   
    print  'Get Page using GET method'

下面,我将利用上一篇的线程池作为辅助,实现多线程下的并行爬取,并用上面自己写的下载页面的方法和urllib2进行一下性能对比。

?
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
113
114
115
116
117
118
119
120
121
122
123
124
'''
Created on 2012-3-16
@author: xiaojay
'''
import fetchPage
import threadpool
import datetime
import statistics
import urllib2
'''one thread'''
def usingOneThread(limit):
    urlset = open("input.txt","r")
    start = datetime.datetime.now()
    for u in urlset:
        if limit <= 0 : break
        limit-=1
        hostname , filename = parse(u)
        res= fetchPage.downPage(hostname,filename,0)
        fetchPage.dealwithResult(res)
    end = datetime.datetime.now()
    print "Start at :\t" , start
    print "End at :\t" , end
    print "Total Cost :\t" , end - start
    print 'Total fetched :', statistics.fetched_url
    
'''threadpoll and GET method'''
def callbackfunc(request,result):
    fetchPage.dealwithResult(result[0],result[1])
def usingThreadpool(limit,num_thread):
    urlset = open("input.txt","r")
    start = datetime.datetime.now()
    main = threadpool.ThreadPool(num_thread)
    for url in urlset :
        try :
            hostname , filename = fetchPage.parse(url)
            req = threadpool.WorkRequest(fetchPage.downPage,args=[hostname,filename],kwds={},callback=callbackfunc)
            main.putRequest(req)
        except Exception:
            print Exception.message       
    while True:
        try:
            main.poll()
            if statistics.total_url >= limit : break
        except threadpool.NoResultsPending:
            print "no pending results"
            break
        except Exception ,e:
            print e
    end = datetime.datetime.now()
    print "Start at :\t" , start   
    print "End at :\t" , end
    print "Total Cost :\t" , end - start
    print 'Total url :',statistics.total_url
    print 'Total fetched :', statistics.fetched_url
    print 'Lost url :', statistics.total_url - statistics.fetched_url
    print 'Error 404 :' ,statistics.failed_url
    print 'Error timeout :',statistics.timeout_url
    print 'Error Try too many times ' ,statistics.trytoomany_url
    print 'Error Other faults ',statistics.other_url
    main.stop()
'''threadpool and urllib2 '''
def downPageUsingUrlib2(url):
    try:
        req = urllib2.Request(url)
        fd = urllib2.urlopen(req)
        f = open("pages3/"+str(abs(hash(url))),'w')
        f.write(fd.read())
        f.flush()
        f.close()
        return url ,'success'
    except Exception:
        return url , None
    
def writeFile(request,result):
    statistics.total_url += 1
    if result[1]!=None :
        statistics.fetched_url += 1
        print statistics.total_url,'\tfetched :', result[0],
    else:
        statistics.failed_url += 1
        print statistics.total_url,'\tLost :',result[0],
def usingThreadpoolUrllib2(limit,num_thread):
    urlset = open("input.txt","r")
    start = datetime.datetime.now()  
    main = threadpool.ThreadPool(num_thread)   
    
    for url in urlset :
        try :
            req = threadpool.WorkRequest(downPageUsingUrlib2,args=[url],kwds={},callback=writeFile)
            main.putRequest(req)
        except Exception ,e:
            print e
        
    while True:
        try:
            main.poll()
            if statistics.total_url  >= limit : break
        except threadpool.NoResultsPending:
            print "no pending results"
            break
        except Exception ,e:
            print e
    end = datetime.datetime.now()   
    print "Start at :\t" , start
    print "End at :\t" , end
    print "Total Cost :\t" , end - start
    print 'Total url :',statistics.total_url
    print 'Total fetched :', statistics.fetched_url
    print 'Lost url :', statistics.total_url - statistics.fetched_url
    main.stop()
if __name__ =='__main__':
    '''too slow'''
    #usingOneThread(100)
    '''use Get method'''
    #usingThreadpool(3000,50)
    '''use urllib2'''
    usingThreadpoolUrllib2(3000,50)

实验分析:

实验数据:larbin抓取下来的3000条url,经过Mercator队列模型(我用c++实现的,以后有机会发个blog)处理后的url集合,具有随机和代表性。使用50个线程的线程池。
实验环境:ubuntu10.04,网络较好,python2.6
存储:小文件,每个页面,一个文件进行存储
PS:由于学校上网是按流量收费的,做网络爬虫,灰常费流量啊!!!过几天,可能会做个大规模url下载的实验,用个几十万的url试试。

实验结果:

使用urllib2 ,usingThreadpoolUrllib2(3000,50)

Start at :    2012-03-16 22:18:20.956054
End at :    2012-03-16 22:22:15.203018
Total Cost :    0:03:54.246964
Total url : 3001
Total fetched : 2442
Lost url : 559
下载页面的物理存储大小:84088kb

使用自己的getPageUsingGet ,usingThreadpool(3000,50)

Start at :    2012-03-16 22:23:40.206730
End at :    2012-03-16 22:26:26.843563
Total Cost :    0:02:46.636833
Total url : 3002
Total fetched : 2484
Lost url : 518
Error 404 : 94
Error timeout : 312
Error Try too many times  0
Error Other faults  112
下载页面的物理存储大小:87168kb

小结: 自己写的下载页面程序,效率还是很不错的,而且丢失的页面也较少。但其实自己考虑一下,还是有很多地方可以优化的,比如文件过于分散,过多的小文件创建和释放定会产生不小的性能开销,而且程序里用的是hash命名,也会产生很多的计算,如果有好的策略,其实这些开销都是可以省略的。另外DNS,也可以不使用python自带的DNS解析,因为默认的DNS解析都是同步的操作,而DNS解析一般比较耗时,可以采取多线程的异步的方式进行,再加以适当的DNS缓存很大程度上可以提高效率。不仅如此,在实际的页面抓取过程中,会有大量的url ,不可能一次性把它们存入内存,而应该按照一定的策略或是算法进行合理的分配。 总之,采集页面要做的东西以及可以优化的东西,还有很多很多。

附件下载:程序代码(水平有限,仅供参考)

相关文章:

从特斯拉到英伟达,那些端到端自动驾驶研发系统有何不同?

作者 | 黄浴&#xff0c;奇点首席科学家兼总裁来源 | 转载自知乎专栏自动驾驶与视觉感知导读&#xff1a;近日&#xff0c;吴恩达的 Drive.ai 被苹果收购的消息给了自动驾驶领域一记警钟&#xff0c;但这个领域的进展和成果犹在。本文将介绍一些端到端的自动驾驶研发系统&#…

vc6中进行多行注释和反注释的方法

1、利用工具中自带的按钮实现&#xff1a; Tools-->Customize-->Add-ins and Macro Files-->将SAMPLE项选中-->Commands-->Category中选择Macros-->在Commands中将CommentOut拖到工具栏&#xff0c;再选中一个Images&#xff0c;点击OK即可。 这种方法是使…

一看就懂!【英雄联盟锐雯】与 Python 详解设计模式之门面模式

【网络配图】设计模式&#xff08;Design Pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式的目的&#xff1a;为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编写真正工程化&#xff1b;设计模…

无法挂载 NTFS格式的分区:mount: unknown filesystem type ‘ntfs’

问题&#xff1a; # mount –t ntfs /dev/sdb1 /mnt/usb mount: unknown filesystem type ‘ntfs’ 这是由于CentOS release 5.3(Final)上无法识别NTFS格式的分区。解决办法&#xff1a; 通过使用 ntfs-3g 来解决。 打开ntfs-3g的下载点http://w…

vs2008 ActiveX控件测试容器的生成以及调试ActiveX控件

1、ActiveX控件测试容器的生成&#xff1a;用TSTCON生成测试容器 http://msdn.microsoft.com/zh-cn/library/f9adb5t5(vvs.90).aspx 在搜索里搜索tstcon,双击TstCon文件夹&#xff0c;将此文件夹里的东西全部复制到另外一个新的空文件里&#xff0c;然后打开解决方案 tstcon.…

百度自动驾驶新突破:获首批T4牌照,升级Apollo 5.0,将进行复杂城市场景路测...

作者 | KYLE WIGGERS等编译 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;最近&#xff0c;百度自动驾驶项目终于有了进展。先是上周悄然发布了 Apollo 的最新版本 Apollo 5.0&#xff0c;引入一些新功能&#xff1b;昨天下午&#xff0c;北京市自动驾驶测试…

折叠屏就要来了,适配逼死 Android 开发?

1. 异型屏还没适配好&#xff0c;折叠屏就要来了&#xff0c;Android 的碎片化&#xff0c;让开发者又多掉了不少头发。 北京时间 11 月 8 日&#xff0c;三星在旧金山向开发者发布了一款可折叠屏幕手机。并表示&#xff0c;新技术需要开发者调整其 App&#xff0c;以确保在手机…

nginx虚拟目录配置

2019独角兽企业重金招聘Python工程师标准>>> 今天搞了N久的虚拟目录配置&#xff0c;在几乎要放弃的时侯偶然看到一篇文章&#xff0c;将我的问题搞定 原贴地址&#xff1a;http://blog.sina.com.cn/s/blog_6c2e6f1f0100l92h.html 我的需求是这样的&#xff0c;系…

阿里达摩院刷新纪录,开放域问答成绩比肩人类水平,超微软、Facebook

出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;近日&#xff0c;由阿里巴巴达摩院语言技术实验室研发的 Multi-Doc Enriched BERT 模型在微软的 MS MARCO 数 据评测任务&#xff0c;Passage Retrieval Task&#xff08;文档检索排序&#xff09;和 Q&A Task&…

解决windows7下vs2008不能正常编译ActiveX控件的问题

在windows7系统中&#xff0c;vs2008环境下用MFC ActiveX Control生成的控件不能正常编译&#xff0c;但是在xp系统中却能正常编译&#xff0c;解决方法&#xff1a; 1、在windows7中编译&#xff0c;提示错误为&#xff1a;fatal error LNK1000: Internal error during IncrB…

什么是shell【TLCL】

常用命令 datecaldf——report file system disk space usagefree——display amount of free and used memory in the systemexit——退出终端Ctrl-Alt-F1 到 Ctrl-Alt-F6 访问后台终端会话&#xff0c;AltF7返回图形桌面转载于:https://www.cnblogs.com/songdechiu/p/9943599…

Linux终端的几个常用快捷方式,记下!

一、初识linux的终端种类&#xff1a;本地、远程    查看本终端命令&#xff1a;     #tty 命令&#xff0c;看到当前所处的终端     #&#xff08;w&#xff09;who 命令&#xff0c;看到系统中所有登录的用户 其中&#xff0c;tty 终端为表示在本地命令行模式下打开…

从0到1 | 文本挖掘的传统与深度学习算法

一、什么是文本挖掘&#xff1f;讨论文本挖掘之前&#xff0c;我们要先说一下数据挖掘的概念&#xff0c;因为文本挖掘是数据挖掘的一个分支。数据挖掘&#xff08;Data Mining&#xff09;指从大量的数据中通过算法搜索隐藏在其中信息的过程。而文本挖掘就是从文本数据中获取有…

window7系统中64位安装matalbR2009b后出现乱码的解决方案

转自&#xff1a;http://blog.csdn.net/shaoguangleo/archive/2010/11/29/6042194.aspx MATLAB 中默认的字体是 Monospaced &#xff08;即等宽字体&#xff09;&#xff0c;这是一种非常适合用于显示程序源代码的字体。但Monospaced 是种逻辑字体&#xff0c;它在不同语言和操…

【电路】pmic芯片设计细节

VIO_IN供电https://e2e.ti.com/support/power-management/f/196/t/712146?tisearche2e-sitesearch&keymatchtps65916 Note that every GPIO will be configured as an input for the first 6ms after VCC is supplied, which is the time it takes for the device to init…

sql server 的一些记录

下面记录一些用到sql server查询时候用到的函数。 charindex,在一个表达式中搜索另一个表达式并返回其起始位置&#xff08;如果找到&#xff09;。http://msdn.microsoft.com/zh-cn/library/ms186323.aspx CHARINDEX ( expressionToFind ,expressionToSearch [ , start_locati…

[WPF] UserControl vs CustomControl

原文:[WPF] UserControl vs CustomControl介绍 WPF中有两种控件&#xff1a;UserControl和CustomControl&#xff0c;但是这两者有什么区别呢&#xff1f;这篇博客中将介绍两者之间的区别&#xff0c;这样可以在项目中合理的使用它们。 UserControl 将多个WPF控件(例如&#xf…

50行Python代码,获取公众号全部文章

作者 | 胖虎转载自Python3X&#xff08;ID: python3xxx &#xff09;爬取公众号的方式常见的有两种&#xff1a;通过搜狗搜索去获取&#xff0c;缺点是只能获取最新的十条推送文章。通过微信公众号的素材管理&#xff0c;获取公众号文章。缺点是需要申请自己的公众号。今天介绍…

解决Windows7下vs2008 Active control test container 不能运行的问题

按照&#xff1a;http://blog.csdn.net/fengbingchun/archive/2011/05/05/6398356.aspx 在windows7系统中&#xff0c;vs2008环境下用MFC ActiveX Control生成的控件不能正常编译&#xff0c;但是在xp系统中却能正常编译&#xff0c;解决方法&#xff1a; 1、在windows7中编译…

入门必备 | 一文读懂神经架构搜索

作者 | Md Ashiqur Rahman编译 | 刘静转载自图灵TOPIA&#xff08;ID: turingtopia&#xff09;近期谷歌大脑团队发布了一项新研究&#xff1a;只靠神经网络架构搜索出的网络&#xff0c;不训练&#xff0c;不调参&#xff0c;就能直接执行任务。这样的网络叫做WANN&#xff0c…

脚本化 tmux — LinuxTOY

脚本化 tmux — LinuxTOY脚本化 tmux2012-07-02 Toy Posted in TipsRSS昨天我在家试了下脚本化 tmux&#xff0c;其表现相当令人满意&#xff0c;只需稍加定制便可满足各种实际需要。这或许可以成为抛弃 GNU screen&#xff0c;改用 tmux 的又一个理由。该脚本先判断一个名为 c…

OpenCV像素点处理

转自&#xff1a;http://blog.csdn.net/hxgqh/archive/2011/02/23/6202001.aspx 获得像素点的值便可以更加灵活的进行图像处理&#xff0c;在OpenCV里可以通过cvGet2D()和cvSet2D()两个函数加上一个CvScalar结构体做到。OpenCV中&#xff0c;CvScalar结构为&#xff1a;typedef…

亲 , Zookeeper了解一下 : 概述

2019独角兽企业重金招聘Python工程师标准>>> 在学习的过程中,我们总需要一个来自灵魂的拷问: 为什么? 为什么会产生Zookeeper 这个问题有深度,那要从五百万年说起,在遥远的塞伯坦星球..... 扯远了... 在遥远在单机单服务的时代 , 想要扩展服务 , 只能增加硬件配置 …

wordpress jquery加载如何实现?

2019独角兽企业重金招聘Python工程师标准>>> 为什么写这篇文章&#xff1f; 因为之前在wordpress里面用jquery的时候没注意&#xff0c;这次因为要优化网站把没用的插件删了&#xff0c;结果发现有的jquery功能失效了&#xff0c;调试了许久才发现可能是jQuery没有定…

百度与华为重磅合作!李彦宏:技术是百度的信仰

导语&#xff1a;这是百度第三年举办AI开发者大会。一生二&#xff0c;二生三&#xff0c;三生万物。AI虽然不能产生万物&#xff0c;但是正在“唤醒万物”。 作者 | 阿司匹林 出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09; 在今天上午举行的的百度AI开发者大…

已知空间一点到另外两点直线的距离

转自&#xff1a;http://www.cnblogs.com/clarkustb/archive/2008/11/04/1326500.html 已知空间一点到另外两点直线的距离设空间中的三点为M1,M2,M3,分别用矢量a,b表示方向向量M1M2和M1M3,则&#xff1a; 1. M3到M1,M2连线的距离为|axb|/|a|&#xff0c;这里|.|表示向量的…

vue---进行post和get请求

参考文档&#xff1a; https://www.jb51.net/article/125717.htm 使用axios <script src"https://unpkg.com/axios/dist/axios.min.js"></script> 基本使用方法&#xff1a; get请求&#xff1a; // Make a request for a user with a given ID axios.ge…

CxImage图像处理类库

转自&#xff1a;http://blog.csdn.net/byxdaz/archive/2009/04/10/4061324.aspx CxImage是一个可以用于MFC 的C图像处理类库类&#xff0c;它可以打开&#xff0c;保存&#xff0c;显示&#xff0c;转换各种常见格式的图像文件&#xff0c;比如BMP, JPEG, GIF, PNG, TIFF, MNG…

设计模式学习2 工厂模式

工厂模式其实就是简单模式的升级版本&#xff0c; 简单模式将界面与业务逻辑区分开&#xff0c;但是如果不停的增加计算器的运算方式&#xff0c;简单模式中的工厂Factory 中判断的业务逻辑会变非常复杂&#xff0c;这不符合封装的原则。 所以在此之上将Factory抽象了出来&…

输入字符串,包含数字,大小写字母,编程输出出现做多的数字的和

题目描述&#xff1a; 输入字符串&#xff0c;包含数字&#xff0c;大小写字母&#xff0c;编程输出出现做多的数字的和。 思路&#xff1a; 1.创建输入对象2.输入字符串3.利用正则将字母分离出&#xff0c;剩余的每一个字符串即为待统计的每一个数字&#xff0c;存入字符串数组…