400 028 6601

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

Python自动化开发学习-TinyScrapy-创新互联

这里通过代码一步一步的演变,最后完成的是一个精简的Scrapy。在Scrapy内部,基本的流程就是这么实现的。主要是为了能通过学习了解Scrapy大致的流程,对之后再要去看Scrapy的源码也是有帮助的。

让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:域名注册、网页空间、营销软件、网站建设、坊子网站维护、网站推广。

Twisted使用

因为Scrapy是基于Twisted实现的,所以先看Twisted怎么用

基本使用

基本使用的示例:

from twisted.web.client import getPage, defer
from twisted.internet import reactor

# 所有任务完成后的回调函数
def all_done(arg):
    """所有爬虫执行完后执行,循环终止"""
    print("All Done")
    reactor.stop()

# 单个任务的回调函数
def callback(contents):
    """每个爬虫获取到结果后执行"""
    print(contents)

deferred_list = []

url_list = [
    'http://www.bing.com',
    'http://www.baidu.com',
    'http://edu.51cto.com',
]

for url in url_list:
    deferred = getPage(bytes(url, encoding='utf-8'))
    deferred.addCallback(callback)
    deferred_list.append(deferred)

dlist = defer.DeferredList(deferred_list)
dlist.addBoth(all_done)

if __name__ == '__main__':
    reactor.run()

在for循环里,创建了对象,还给对象加了回调函数,这是单个任务完成后执行的。此时还没有进行下载,而是把所有的对象加到一个列表里。
之后的defer.DeferredList的调用,才是执行所有的任务。并且又加了一个回调函数all_done,这个是所有任务都完成后才执行的。

基于装饰器1

基于装饰器也可以实现,下面的代码是基于上面的示例做了简单的转换:

from twisted.web.client import getPage, defer
from twisted.internet import reactor

def all_done(arg):
    print("All Done")
    reactor.stop()

def one_done(response):
    print(response)

@defer.inlineCallbacks
def task(url):
    deferred = getPage(bytes(url, encoding='utf-8'))
    deferred.addCallback(one_done)
    yield deferred

deferred_list = []

url_list = [
    'http://www.bing.com',
    'http://www.baidu.com',
    'http://edu.51cto.com',
]

for url in url_list:
    deferred = task(url)
    deferred_list.append(deferred)

dlist = defer.DeferredList(deferred_list)
dlist.addBoth(all_done)

if __name__ == '__main__':
    reactor.run()

把原来for循环里的2行代码封装的了一个task函数里,并且加了装饰器。
这个task函数有3个要素:装饰器、deferred对象、通过yield返回返回对象。这个是Twisted里标准的写法。

基于装饰器2

在上面的示例的基础上,把整个for循环都移到task函数里了:

from twisted.web.client import getPage, defer
from twisted.internet import reactor

def all_done(arg):
    print("All Done")
    reactor.stop()

def one_done(response):
    print(response)

@defer.inlineCallbacks
def task():
    for url in url_list:
        deferred = getPage(bytes(url, encoding='utf-8'))
        deferred.addCallback(one_done)
        yield deferred

url_list = [
    'http://www.bing.com',
    'http://www.baidu.com',
    'http://edu.51cto.com',
]

ret = task()
ret.addBoth(all_done)

if __name__ == '__main__':
    reactor.run()

上面说个的3要素:装饰器、deferred对象、yield都有。

基于装饰器永不退出

在前面的示例中,每完成一个任务,就会返回并执行一个回调函数one_done。所有任务如果都返回了,程序就会退出(退出前会执行回调函数all_done)。
这里所做的,就是添加一个不会返回的任务,这样程序的一直不会退出了:

from twisted.web.client import getPage, defer
from twisted.internet import reactor

def all_done(arg):
    print("All Done")
    reactor.stop()

def one_done(response):
    print(response)

@defer.inlineCallbacks
def task():
    for url in url_list:
        deferred = getPage(bytes(url, encoding='utf-8'))
        deferred.addCallback(one_done)
        yield deferred
    # 下面的这个任务永远不会完成
    stop_deferred = defer.Deferred()  # 这是一个空任务,不会去下载,所以永远不会返回
    # stop_deferred.callback(None)  # 执行这句可以让这个任务返回
    stop_deferred.addCallback(lambda s: print(s))
    stop_deferred.callback("stop_deferred")
    yield stop_deferred

url_list = [
    'http://www.bing.com',
    'http://www.baidu.com',
    'http://edu.51cto.com',
]

ret = task()
ret.addBoth(all_done)

if __name__ == '__main__':
    reactor.run()

这里的做法,就是加了一个额外的任务。要求返回的是Deferred对象,这里就创建了一个空的Deferred对象,并把这个对象返回。
在这里,我们并没有让这个空的Deferred对象去下载,所以也就永远不会有返回。
永不退出的意义
这里目的就是不让程序退出,让这个事件循环一直在那里执行。之后还可以继续往里面添加任务,然后执行新的任务。
程序退出的方法
还是可以让程序退出的。就是调用stop_deferred的callback方法,在上面的代码里注释掉了。执行这个方法,就是强制执行该任务的回调函数。
之前都是等任务执行完返回后,会自动调用callback方法,这里就是强制调用了。
并且由于代码里没有为stop_deferred指定回调函数,所有调用方法后不会执行任何函数。不过调用callback方法必须有一个参数,这里随便写个就好了。
也可以给stop_deferred加一个回调函数,然后再调用callback方法:

stop_deferred.addCallback(lambda s: print(s))
stop_deferred.callback("stop_deferred")

Scrapy里的做法
这就是Scrapy里运行完终止的逻辑。第一次只有一个url,执行完就返回了,并且此时应该是所有任务都返回了,那么就会退出程序。
在Scrapy里,也是这样加了一个永远不会返回的任务,不让程序退出。然后之前的结果返回后,又会生成新的任务到调度器,这样就会动态的添加任务继续执行。
要让程序可以退出,这里还需要做一个检测。在下载完成之后的回调函数里,会生成新的任务继续给执行。这里可以执行2个回调函数。
第一个回调函数就是生成新的任务放入调度器,第二个回调函数就是检测等待执行的任务的数量,以及正在执行的任务数量。如果都是0,表示程序可以结束了。
程序结束的方法就是上面的用的调用执行callback方法。

执行完毕后停止事件循环

基于上面的说的,这里的代码实现了全部任务执行完毕后可以调用stop_deferred的callback方法来退出:

from twisted.web.client import getPage, defer
from twisted.internet import reactor

task_list = []
stop_deferred = None

def all_done(arg):
    print("All Done")
    reactor.stop()

def one_done(response):
    print(response)

def check_empty(response, *args, **kw):
    url = kw.get('url')
    if url in running_list:
        running_list.remove(url)
    if not running_list:
        stop_deferred.callback()

@defer.inlineCallbacks
def task():
    global running_list, stop_deferred  # 全局变量
    running_list = url_list.copy()
    for url in url_list:
        deferred = getPage(bytes(url, encoding='utf-8'))
        deferred.addCallback(one_done)
        deferred.addCallback(check_empty, url=url)
        yield deferred
    stop_deferred = defer.Deferred()
    yield stop_deferred

url_list = [
    'http://www.bing.com',
    'http://www.baidu.com',
    'http://edu.51cto.com',
]

ret = task()
ret.addBoth(all_done)

if __name__ == '__main__':
    reactor.run()

代码优化

上面的代码功能上都实现了,但是实现方法有点不太好。
首先,task函数里分成了两部分,一部分是我们自己调度的任务,一部分是为了不让程序退出,而加的一个空任务。可以把这两部分拆开放在两个函数里。分拆之后,只有第一部分的函数是需要留给用户使用的。下面是把原来的task函数分拆后的代码,并且每个函数也都需要加上装饰器:

from twisted.web.client import getPage, defer
from twisted.internet import reactor

task_list = []
stop_deferred = None

def all_done(arg):
    print("All Done")
    reactor.stop()

def one_done(response):
    print(response)

def check_empty(response, url):
    if url in running_list:
        running_list.remove(url)
    if not running_list:
        stop_deferred.callback()

@defer.inlineCallbacks
def open_spider():
    global running_list
    running_list = url_list.copy()
    for url in url_list:
        deferred = getPage(bytes(url, encoding='utf-8'))
        deferred.addCallback(one_done)
        deferred.addCallback(check_empty, url)
        yield deferred

@defer.inlineCallbacks
def stop():
    global stop_deferred
    stop_deferred = defer.Deferred()
    yield stop_deferred

@defer.inlineCallbacks
def task():
    yield open_spider()
    yield stop()

url_list = [
    'http://www.bing.com',
    'http://www.baidu.com',
    'http://edu.51cto.com',
]

ret = task()
ret.addBoth(all_done)

if __name__ == '__main__':
    reactor.run()

另外还有全局变量的问题,这里的代码使用了全部变量,这不是一个好的做法。再改下去需要引入class了。

模拟Scrapy

从这里开始,就要使用面向对象的方法,进一步进行封装了。

封装部分

先把之前主要的代码封装到类里:

from twisted.web.client import getPage, defer
from twisted.internet import reactor
import queue

class Request(object):
    """封装请求的url和回调函数"""
    def __init__(self, url, callback):
        self.url = url
        self.callback = callback

class Scheduler(object):
    """调度器"""
    def __init__(self, engine):
        self.engine = engine
        self.q = queue.Queue()

    def enqueue_request(self, request):
        """添加任务"""
        self.q.put(request)

    def next_request(self):
        """获取下一个任务"""
        try:
            req = self.q.get(block=False)
        except queue.Empty:
            req = None
        return req

    def size(self):
        return self.q.qsize()

class ExecutionEngine(object):
    """引擎"""
    def __init__(self):
        self._close_wait = None  # stop_deferred
        self.start_requests = None
        self.scheduler = Scheduler(self)
        self.in_progress = set()  # 正在执行中的任务

    def _next_request(self):
       while self.start_requests:
            request = next(self.start_requests, None)
            if request:
                self.scheduler.enqueue_request(request)
            else:
                self.start_requests = None
       while len(self.in_progress) < 5 and self.scheduler.size() > 0:  # 大编发为5
            request = self.scheduler.next_request()
            if not request:
                break
            self.in_progress.add(request)
            d = getPage(bytes(request.url, encoding='utf-8'))
            # addCallback是正确返回的时候执行,还有addErrback是返回有错误的时候执行
            # addBoth就是上面两种情况返回都会执行
            d.addBoth(self._handle_downloader_output, request)
            d.addBoth(lambda x, req: self.in_progress.remove(req), request)
            d.addBoth(lambda x: self._next_request())
        if len(self.in_progress) == 0 and self.scheduler.size() == 0:
            self._close_wait.callback(None)

    def _handle_downloader_output(self, response, request):
        import types
        gen = request.callback(response)
        if isinstance(gen, types.GeneratorType):  # 是否为生成器类型
            for req in gen:
                # 这里还可以再加判断,如果是request对象则继续爬取
                # 如果是item对象,则可以交给pipline
                self.scheduler.enqueue_request(req)

    @defer.inlineCallbacks
    def open_spider(self, start_requests):
        self.start_requests = start_requests
        yield None
        reactor.callLater(0, self._next_request)  # 过多少秒之后,执行后面的函数

    @defer.inlineCallbacks
    def start(self):
        """原来的stop函数"""
        self._close_wait = defer.Deferred()
        yield self._close_wait

@defer.inlineCallbacks
def crawl(start_requests):
    """原来的task函数"""
    engine = ExecutionEngine()
    start_requests = iter(start_requests)
    yield engine.open_spider(start_requests)
    yield engine.start()

def all_done(arg):
    print("All Done")
    reactor.stop()

def one_done(response):
    print(response)

count = 0
def chouti(response):
    """任务返回后生成新的Request继续交给调度器执行"""
    global count
    count += 1
    print(response)
    if count > 3:
        return None
    for i in range(10):
        yield Request("http://dig.chouti.com/all/hot/recent/%s" % i, lambda x: print(len(x)))

if __name__ == '__main__':
    url_list = [
        'http://www.bing.com',
        'https://www.baidu.com',
        'http://edu.51cto.com',
    ]
    requests = [Request(url, callback=one_done) for url in url_list]
    # requests = [Request(url, callback=chouti) for url in url_list]
    ret = crawl(requests)
    ret.addBoth(all_done)
    reactor.run()

这里还写了一个回调函数chouti,可以在爬虫返回后,生成新的Request继续爬取。为了控制这个回调函数的调用,又加了一个全局变量。
接下来会对这部分函数继续封装,把所有的代码都封装到类里。
闭包解决全局变量
这里的部分是我自己尝试的思考。
其实还可以通过闭包的方法。通过闭包来保存函数的状态,而不使用全局变量:

def chouti2():
    n = 0

    def func(response):
        print(response)
        nonlocal n
        n += 1
        if n > 3:
            return None
        for i in range(10):
            yield Request("http://dig.chouti.com/all/hot/recent/%s" % i, lambda x: print(len(x)))
    return func

if __name__ == '__main__':
    url_list = [
        'http://www.bing.com',
        'https://www.baidu.com',
        'http://edu.51cto.com',
    ]
    # requests = [Request(url, callback=one_done) for url in url_list]
    # requests = [Request(url, callback=chouti) for url in url_list]
    callback = chouti2()
    requests = [Request(url, callback=callback) for url in url_list]
    ret = crawl(requests)
    ret.addBoth(all_done)
    reactor.run()

完全封装

上面的示例还有几个函数,继续把剩下的函数也封装到类里。下面的这个就是TinyScrapy

from twisted.web.client import getPage, defer
from twisted.internet import reactor
import queue

class Request(object):
    """封装请求的url和回调函数"""
    def __init__(self, url, callback=None):
        self.url = url
        self.callback = callback  # 默认是None,则会去调用Spider对象的parse方法

class Scheduler(object):
    """调度器"""
    def __init__(self, engine):
        self.engine = engine
        self.q = queue.Queue()

    def enqueue_request(self, request):
        """添加任务"""
        self.q.put(request)

    def next_request(self):
        """获取下一个任务"""
        try:
            req = self.q.get(block=False)
        except queue.Empty:
            req = None
        return req

    def size(self):
        return self.q.qsize()

class ExecutionEngine(object):
    """引擎"""
    def __init__(self):
        self._close_wait = None  # stop_deferred
        self.start_requests = None
        self.scheduler = Scheduler(self)
        self.in_progress = set()  # 正在执行中的任务
        self.spider = None  # 在open_spider方法里添加

    def _next_request(self):
       while self.start_requests:
            request = next(self.start_requests, None)
            if request:
                self.scheduler.enqueue_request(request)
            else:
                self.start_requests = None
       while len(self.in_progress) < 5 and self.scheduler.size() > 0:  # 大编发为5
            request = self.scheduler.next_request()
            if not request:
                break
            self.in_progress.add(request)
            d = getPage(bytes(request.url, encoding='utf-8'))
            # addCallback是正确返回的时候执行,还有addErrback是返回有错误的时候执行
            # addBoth就是上面两种情况返回都会执行
            d.addBoth(self._handle_downloader_output, request)
            d.addBoth(lambda x, req: self.in_progress.remove(req), request)
            d.addBoth(lambda x: self._next_request())
        if len(self.in_progress) == 0 and self.scheduler.size() == 0:
            self._close_wait.callback(None)

    # 这个方法和之前的有一点小的变化,主要是用到了新定义的Response对象
    def _handle_downloader_output(self, body, request):
        import types
        response = Response(body, request)
        # 如果没有指定callback就调用Spider类的parse方法
        func = request.callback or self.spider.parse
        gen = func(response)
        if isinstance(gen, types.GeneratorType):  # 是否为生成器类型
            for req in gen:
                # 这里还可以再加判断,如果是request对象则继续爬取
                # 如果是item对象,则可以交给pipline
                self.scheduler.enqueue_request(req)

    @defer.inlineCallbacks
    def open_spider(self, spider, start_requests):
        self.start_requests = start_requests
        self.spider = spider  # 加了这句
        yield None
        reactor.callLater(0, self._next_request)  # 过多少秒之后,执行后面的函数

    @defer.inlineCallbacks
    def start(self):
        """原来的stop函数"""
        self._close_wait = defer.Deferred()
        yield self._close_wait

class Response(object):
    def __init__(self, body, request):
        self.body = body
        self.request = request
        self.url = request.url

    @property
    def text(self):
        return self.body.decode('utf-8')

class Crawler(object):
    def __init__(self, spider_cls):
        self.spider_cls = spider_cls
        self.spider = None
        self.engine = None

    @defer.inlineCallbacks
    def crawl(self):
        self.engine = ExecutionEngine()
        self.spider = self.spider_cls()
        start_requests = iter(self.spider.start_requests())
        yield self.engine.open_spider(self.spider, start_requests)
        yield self.engine.start()

class CrawlerProcess(object):
    def __init__(self):
        self._active = set()
        self.crawlers = set()

    def crawl(self, spider_cls, *args, **kwargs):
        crawler = Crawler(spider_cls)
        self.crawlers.add(crawler)
        d = crawler.crawl(*args, **kwargs)
        self._active.add(d)
        return d

    def start(self):
        dl = defer.DeferredList(self._active)
        dl.addBoth(self._stop_reactor)
        reactor.run()

    @classmethod
    def _stop_reactor(cls, _=None):
        """原来的all_done函数
        之前的示例中,这个函数都是要接收一个参数的。
        虽然不用,但是调用的模块一定会传过来,所以一定要接收一下。
        这里就用了占位符来接收这个参数,并且设置了默认值None。
        """
        print("All Done")
        reactor.stop()

class Spider(object):
    def __init__(self):
        if not hasattr(self, 'start_urls'):
            self.start_urls = []

    def start_requests(self):
        for url in self.start_urls:
            yield Request(url)

    def parse(self, response):
        print(response.body)

class ChoutiSpider(Spider):
    name = "chouti"
    start_urls = ["http://dig.chouti.com"]

    def parse(self, response):
        print(next((s for s in response.text.split('\n') if "" in s)))

class BingSpider(Spider):
    name = "bing"
    start_urls = ["http://www.bing.com"]

class BaiduSpider(Spider):
    name = "baidu"
    start_urls = ["http://www.baidu.com"]

if __name__ == '__main__':
    spider_cls_list = [ChoutiSpider, BingSpider, BaiduSpider]
    crawler_process = CrawlerProcess()
    for spider_cls in spider_cls_list:
        crawler_process.crawl(spider_cls)
    crawler_process.start()</code></pre><p>这里用的类名、方法名、部分代码都是和Scrapy的源码里一样的。相当于把Scrapy精简了,把其中的核心都提取出来了。如果能看明白这部分代码,再去Scrapy里看源码应该能相对容易一些了。</p><p align=center>另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、<a href="https://www.cdcxhl.com/gaofang/" target="_blank">高防服务器</a>、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。</p>        <br>
        本文标题:Python自动化开发学习-TinyScrapy-创新互联        <br>
        分享路径:<a href="http://mbwzsj.com/article/ddiijp.html">http://mbwzsj.com/article/ddiijp.html</a>
    </div>
    <div class="other">
        <h3>其他资讯</h3>
        <ul>
            <li><a href="/article/ddpoojs.html">什么书籍介绍SAP系统的的简单介绍</a></li><li><a href="/article/ddpooeo.html">c语言mypow函数 c语言数学函数pow用法例子</a></li><li><a href="/article/ddpoodh.html">显示域名到期怎么处理 域名到期怎么办</a></li><li><a href="/article/ddpoojp.html">go语言写命令行 go语言命令行参数</a></li><li><a href="/article/ddpoogo.html">关于itoa函数c语言实现的信息</a></li>        </ul>
    </div>
</div>
<div class="oneE">
    <div class="oneEa container wow fadeInUp">
        <ul>
            <li>
                <dd><img src="/Public/Home/img/oe1.png" alt=""></dd>
                <h3>网站建设专属方案</h3>
            </li>
            <li>
                <dd><img src="/Public/Home/img/oe2.png" alt=""></dd>
                <h3>网站定制化设计</h3>
            </li>
            <li>
                <dd><img src="/Public/Home/img/oe3.png" alt=""></dd>
                <h3>7X24小时服务</h3>
            </li>
            <li>
                <dd><img src="/Public/Home/img/oe4.png" alt=""></dd>
                <h3>N对管家服务</h3>
            </li>
        </ul>
    </div>
    <div class="oneEb container wow fadeInUp">
        <h2>让你的专属顾问为你服务</h2>
        <form action="">
            <input type="text" placeholder="需求">
            <input type="text" placeholder="输入你的联系方式(微信或电话号码)">
            <button>立即联系</button>
        </form>
    </div>
</div>
<footer>
    <div class="foot container">
        <div class="footl">
            <img src="/Public/Home/img/logo.png" alt="">
            <p>用前卫的视觉</p>
            <p>把握好每一个细节</p>
        </div>
        <div class="footc">
            <dl>
                <dt>服务项目</dt>
                <dd><a href="">网站建设</a></dd>
                <dd><a href="">网站优化</a></dd>
                <dd><a href="">网站设计</a></dd>
                <dd><a href="">小程序开发</a></dd>
                <dd><a href="">电商平台</a></dd>
            </dl>
            <dl>
                <dt>客户案例</dt>
                <dd><a href="">网站案例</a></dd>
                <dd><a href="">优化案例</a></dd>
                <dd><a href="">外贸网站案例</a></dd>
            </dl>
            <dl>
                <dt>资讯中心</dt>
                <dd><a href="">建站动态</a></dd>
                <dd><a href="">网站知识</a></dd>
                <dd><a href="">网站运营</a></dd>
            </dl>
            <dl>
                <dt>快捷导航</dt>
                <dd><a href="">关于翔捷宏鑫</a></dd>
                <dd><a href="">联系方式</a></dd>
            </dl>
        </div>
        <div class="footr">
            <h3>联系方式</h3>
            <p>地址:成都市太升南路288号锦天国际A幢1002号</p>
            <div class="tel">
                <i><img src="/Public/Home/img/ftel.png" alt=""></i><a href="tel:13518219792">电话:13518219792</a>
            </div>
        </div>
    </div>
    <div class="yqlink container">
        标签:
        <a href="http://www.zsjierui.cn/" target="_blank">资阳</a>
        <a href="http://www.wzjierui.cn/" target="_blank">温江</a>
        <a href="http://www.ndjierui.cn/" target="_blank">南部</a>
        <a href="http://www.ptjierui.cn/" target="_blank">郫县</a>
        <a href="http://www.hzjierui.cn/" target="_blank">彭州</a>
        <a href="http://www.ncjierui.cn/" target="_blank">彭山</a>
        <a href="http://www.whjierui.cn/" target="_blank">乐山</a>
        <a href="http://www.ahjierui.cn/" target="_blank">简阳</a>
        <a href="http://www.csjierui.cn/" target="_blank">绵阳</a>
        <a href="http://www.qhjierui.cn/" target="_blank">德阳</a>
        <a href="http://www.scjierui.cn/" target="_blank">四川</a>
        <a href="http://www.tjjierui.cn/" target="_blank">什邡</a>
        <a href="http://www.tyjierui.cn/" target="_blank">绵竹</a>
        <a href="http://www.xzjierui.cn/" target="_blank">眉山</a>
        <a href="http://www.sxjierui.cn/" target="_blank">双流</a>
        <a href="http://www.ptruijie.cn/" target="_blank">新都</a>
        <a href="http://www.xjjierui.cn/" target="_blank">新津</a>
        <a href="http://www.jljierui.cn/" target="_blank">龙泉</a>
        <a href="http://www.gyruijie.cn/" target="_blank">广汉</a>
        <a href="http://www.csruizhi.cn/" target="_blank">崇州</a>
        <a href="http://www.zjjierui.cn/" target="_blank">广元</a>
        <a href="http://www.zzjierui.cn/" target="_blank">广安</a>
        <a href="http://www.hnjierui.cn/" target="_blank">巴中</a>
        <a href="http://www.fjjierui.cn/" target="_blank">达州</a>
        <a href="http://www.gyjierui.cn/" target="_blank">南充</a>
        <a href="http://www.fzjierui.cn/" target="_blank">遂宁</a>
        <a href="http://www.cdjierui.cn/" target="_blank">广安</a>
        <a href="http://www.jxjierui.cn/" target="_blank">内江</a>
        <a href="http://www.jxruijie.cn/" target="_blank">自贡</a>
        <a href="http://www.hyruijie.cn/" target="_blank">泸州</a>
        <a href="http://www.gzruizhi.cn/" target="_blank">宜宾</a>
    </div>
    <div class="copy container">
        <div class="copyl">
            © Copyright 2025 青羊区翔捷宏鑫字牌设计制作工作室(个体工商户) 版权所有 <a href="https://beian.miit.gov.cn/" target="_blank" rel="nofollow" style="color:#FFFFFF">蜀ICP备2025123194号-11</a>
            <a href="https://www.cdcxhl.com/menu.html">网站地图</a>
            <a href="https://www.cdcxhl.com/articles/" rel="nofollow">其他文章分类</a>
            <a href="http://www.mbwzsj.com/">马边翔捷宏鑫建站</a>
        </div>
        <div class="copyr">
            <i><img src="/Public/Home/img/foot1.png" alt=""></i>
            <i><img src="/Public/Home/img/foot2.png" alt=""></i>
            <i><img src="/Public/Home/img/foot3.png" alt=""></i>
            <i><img src="/Public/Home/img/foot4.png" alt=""></i>
        </div>
    </div>
    <div class="bq_tag container">
        热门推荐:
        <a href="https://www.cdxwcx.com/jifang/xiyun.html" target="_blank">成都移动服务器托管</a><a href="https://www.cdcxhl.com/idc/cqst.html" target="_blank">重庆水土机房托管</a><a href="http://seo.cdcxhl.cn/" target="_blank">关键词排名优化</a><a href="http://www.xaruijie.cn/" target="_blank">武侯区工商注册</a><a href="http://www.scarg.cn/" target="_blank">成都艾瑞格</a><a href="http://www.schdfang.cn/" target="_blank">气球装饰</a><a href="http://www.dlganxi.com/" target="_blank">公司网站维护</a><a href="http://www.4006tel.net/mobile/" target="_blank">Android开发</a><a href="https://www.cdxwcx.com/wangzhan/case/dfjg.html" target="_blank">东电技服</a><a href="http://www.dmvi.cn/" target="_blank">成都广告制作</a><a href="http://www.zzdkfx.cn/" target="_blank">景区公园导视牌</a><a href="https://www.cdcxhl.com/mobile.html" target="_blank">手机网站</a>    </div>
</footer>
<div class="footbarline"></div>
<div id="footbar" class="uin0">
    <ul>
        <li class="on" data-href="/"><a><i><svg t="1638436981291" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2991" width="48" height="48"><path d="M958.400956 451.54921c-0.058328-5.760191-2.597151-11.215436-6.965645-14.97097L524.345166 69.511143c-7.498788-6.445806-18.581194-6.445806-26.079982 0L309.582871 231.6755l0-102.017488c0-11.04966-8.901741-19.532869-19.951401-19.532869l-88.034009 0c-11.048637 0-19.928888 8.482185-19.928888 19.532869l0 211.954343L71.176063 436.57824c-4.423753 3.800559-6.967692 9.341762-6.967692 15.173584l0 105.500822c0 7.819083 4.554736 14.921851 11.660574 18.183128 2.670829 1.226944 5.51562 1.824555 8.343015 1.824555 4.699022 0 9.346879-1.654686 13.048177-4.836145l53.29788-45.825698 0 324.100516c0 60.677964 49.364291 110.042255 110.042255 110.042255L764.792447 960.741257c60.677964 0 110.042255-49.364291 110.042255-110.042255L874.834702 527.026228l51.585889 44.335764c5.955642 5.119601 14.356986 6.282077 21.481244 2.965541 7.122211-3.313465 11.645225-10.488889 11.565407-18.342764L958.400956 451.54921zM221.578538 150.034085l48.095391 0 0 115.941616-48.095391 41.336454L221.578538 150.034085zM570.718333 920.725892 436.666244 920.725892 436.666244 700.642404c0-11.031241 8.976442-20.007683 20.007683-20.007683l94.0357 0c11.031241 0 20.007683 8.976442 20.007683 20.007683L570.71731 920.725892zM834.818313 495.895207l0 354.803795c0 38.612413-31.414477 70.02689-70.02689 70.02689l-154.058748 0L610.732675 700.642404c0-33.096792-26.926256-60.023048-60.023048-60.023048l-94.0357 0c-33.096792 0-60.023048 26.926256-60.023048 60.023048l0 220.084511L260.59925 920.726915c-38.612413 0-70.02689-31.414477-70.02689-70.02689L190.57236 495.895207c0-1.172709-0.121773-2.314719-0.315178-3.432169l322.113255-276.958846 322.70268 277.348726C834.921667 493.848595 834.818313 494.858598 834.818313 495.895207zM525.411451 173.947727c-7.502881-6.445806-18.587334-6.446829-26.086122 0.00307L104.223736 513.663896l0-52.726875 407.081439-349.870436 407.176606 349.9523 0.521886 51.205219L525.411451 173.947727z" p-id="2992" fill="#2c2c2c"></path></svg><p>首页</p></i></a></li>
        <li><a href="tel:13518219792"><i><svg t="1638437906526" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4519" width="48" height="48"><path d="M705.74 604.873333a53.4 53.4 0 0 0-75.426667 0l-37.713333 37.713334c-21.333333 21.333333-90.413333 0.1-150.846667-60.34S360.046667 452.76 381.413333 431.4l0.046667-0.046667 37.666667-37.666666a53.4 53.4 0 0 0 0-75.426667l-165.94-165.933333a53.393333 53.393333 0 0 0-75.42 0l-37.713334 37.713333c-27.866667 27.866667-44.84 64.52-50.46 108.946667-5.213333 41.206667-0.406667 87.42 14.28 137.333333C133.333333 536.586667 199.773333 642 290.9 733.1S487.42 890.666667 587.653333 920.126667c36.926667 10.86 71.813333 16.32 104.146667 16.32a264.333333 264.333333 0 0 0 33.213333-2.04c44.426667-5.62 81.08-22.593333 108.946667-50.46l37.713333-37.713334a53.393333 53.393333 0 0 0 0-75.42z m135.76 211.193334l-37.706667 37.713333c-42.58 42.573333-115.06 51.6-204.1 25.413333-93.506667-27.5-192.453333-90.1-278.62-176.266666s-148.766667-185.113333-176.266666-278.62c-26.186667-89.033333-17.16-161.52 25.413333-204.1l37.713333-37.706667a10.666667 10.666667 0 0 1 15.086667 0l165.933333 165.933333a10.666667 10.666667 0 0 1 0 15.086667l-37.713333 37.706667C329.113333 423.333333 324.666667 458.82 338.766667 501.073333c12.426667 37.273333 38.286667 76.813333 72.813333 111.333334s74.073333 60.386667 111.333333 72.813333c16.213333 5.406667 31.42 8.08 45.26 8.08 22.233333 0 40.946667-6.913333 54.586667-20.553333l37.706667-37.713334a10.666667 10.666667 0 0 1 15.086666 0l165.933334 165.933334a10.666667 10.666667 0 0 1 0.013333 15.1zM576 234.666667a21.333333 21.333333 0 0 1 21.333333-21.333334 213.333333 213.333333 0 0 1 213.333334 213.333334 21.333333 21.333333 0 0 1-42.666667 0c0-94.106667-76.56-170.666667-170.666667-170.666667a21.333333 21.333333 0 0 1-21.333333-21.333333z m0 128a21.333333 21.333333 0 0 1 21.333333-21.333334 85.426667 85.426667 0 0 1 85.333334 85.333334 21.333333 21.333333 0 0 1-42.666667 0 42.713333 42.713333 0 0 0-42.666667-42.666667 21.333333 21.333333 0 0 1-21.333333-21.333333z m362.666667 64a21.333333 21.333333 0 0 1-42.666667 0c0-164.666667-134-298.666667-298.666667-298.666667a21.333333 21.333333 0 0 1 0-42.666667 341.073333 341.073333 0 0 1 341.333334 341.333334z" fill="#2c2c2c" p-id="4520"></path></svg><p>电话</p></i></a></li>
        <li><a class="opwx"><i><svg t="1638438138558" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10851" width="48" height="48"><path d="M498.816 345.056c26.336 0 43.936-17.632 43.936-43.904 0-26.56-17.568-43.744-43.936-43.744s-52.832 17.184-52.832 43.744C446.016 327.424 472.48 345.056 498.816 345.056zM253.088 257.408c-26.336 0-52.96 17.184-52.96 43.744 0 26.272 26.624 43.904 52.96 43.904 26.24 0 43.808-17.632 43.808-43.904C296.864 274.592 279.328 257.408 253.088 257.408zM1024 626.112c0-138.88-128.832-257.216-286.976-269.536 0.224-1.728 0.32-3.52-0.064-5.312-31.712-147.84-190.688-259.296-369.824-259.296C164.704 91.968 0 233.12 0 406.624c0 93.088 47.52 176.96 137.568 243.104l-31.392 94.368c-2.016 6.144-0.192 12.896 4.704 17.152 2.976 2.56 6.72 3.904 10.496 3.904 2.432 0 4.896-0.576 7.168-1.696L246.4 704.48l14.528 2.944c36.288 7.456 67.616 13.92 106.208 13.92 11.36 0 22.88-0.512 34.176-1.472 4.576-0.384 8.448-2.688 11.072-6.016 42.496 106.336 159.616 183.104 297.44 183.104 35.296 0 71.04-8.512 103.104-16.544l90.848 49.664c2.4 1.312 5.056 1.984 7.68 1.984 3.584 0 7.168-1.216 10.048-3.552 5.056-4.096 7.136-10.848 5.248-17.024l-23.2-77.152C981.344 772.864 1024 699.328 1024 626.112zM398.592 687.968c-10.4 0.896-20.96 1.344-31.424 1.344-35.328 0-65.216-6.112-99.776-13.248L247.296 672c-3.456-0.736-7.104-0.256-10.272 1.376l-88.288 44.192 22.944-68.928c2.24-6.752-0.224-14.112-6.016-18.176C76.96 568.64 32 493.312 32 406.624c0-155.84 150.336-282.656 335.136-282.656 163.36 0 308 99.392 337.856 231.584-171.296 2.24-309.888 122.656-309.888 270.56 0 21.504 3.264 42.336 8.768 62.432C402.208 688.128 400.448 687.808 398.592 687.968zM875.456 815.552c-5.344 4.032-7.616 10.976-5.696 17.376l15.136 50.336-62.112-33.984c-2.368-1.312-5.024-1.984-7.68-1.984-1.312 0-2.624 0.16-3.904 0.512-33.312 8.416-67.776 17.088-101.344 17.088-155.904 0-282.72-107.136-282.72-238.816 0-131.68 126.816-238.784 282.72-238.784 152.928 0 282.144 109.344 282.144 238.784C992 691.744 950.624 759.04 875.456 815.552zM612.992 511.968c-17.568 0-35.136 17.696-35.136 35.232 0 17.664 17.568 35.104 35.136 35.104 26.4 0 43.84-17.44 43.84-35.104C656.832 529.632 639.392 511.968 612.992 511.968zM806.016 511.968c-17.312 0-34.88 17.696-34.88 35.232 0 17.664 17.568 35.104 34.88 35.104 26.304 0 44.064-17.44 44.064-35.104C850.08 529.632 832.352 511.968 806.016 511.968z" p-id="10852" fill="#2c2c2c"></path></svg><p>微信</p></i></a></li>
        <li data-href="/about/"><a><i><svg t="1638438056011" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9170" width="48" height="48"><path d="M896 405.333333v128c0 34.133333-29.866667 64-64 64S768 567.466667 768 533.333333v-128c0-17.066667 8.533333-34.133333 17.066667-42.666666C733.866667 251.733333 640 170.666667 516.266667 170.666667H512c-128 0-221.866667 81.066667-273.066667 192 8.533333 8.533333 17.066667 25.6 17.066667 42.666666v128c0 34.133333-29.866667 64-64 64S128 567.466667 128 533.333333v-128C128 371.2 157.866667 341.333333 192 341.333333h4.266667c51.2-123.733333 174.933333-213.333333 315.733333-213.333333s264.533333 89.6 315.733333 213.333333h4.266667c34.133333 0 64 29.866667 64 64zM896 896H128c0-98.133333 170.666667-213.333333 384-213.333333s384 115.2 384 213.333333z m-59.733333-42.666667c-42.666667-59.733333-170.666667-128-324.266667-128s-281.6 68.266667-324.266667 128h648.533334zM512 682.666667c-119.466667 0-213.333333-93.866667-213.333333-213.333334s93.866667-213.333333 213.333333-213.333333 213.333333 93.866667 213.333333 213.333333-93.866667 213.333333-213.333333 213.333334z m170.666667-213.333334c0-93.866667-76.8-170.666667-170.666667-170.666666s-170.666667 76.8-170.666667 170.666666 76.8 170.666667 170.666667 170.666667 170.666667-76.8 170.666667-170.666667z" fill="#2c2c2c" p-id="9171"></path></svg><p>联系</p></i></a></li>
    </ul>
    <div class="fbrbg"><img src="/Public/Home/img/fbarbg.png"></div>
</div>
</body>
</html>
<script src="/Public/Home/js/jquery.min.js"></script>
<script src="/Public/Home/js/wow.min.js"></script>
<script src="/Public/Home/js/common.js"></script>
<script>
    $(".ny_con img").each(function(){
        var src = $(this).attr("src");    //获取图片地址
        var str=new RegExp("http");
        var result=str.test(src);
        if(result==false){
            var url = "https://www.cdcxhl.com"+src;    //绝对路径
            $(this).attr("src",url);
        }
    });
</script>