Git Product home page Git Product logo

wechat_articles_spider's Introduction

微信公众号文章爬虫(微信文章阅读点赞的获取)

安装

pip install wechatarticles

展示地址:

日更,获取公众号的最新文章链接,支持日更阅读点赞评论正文

注:本项目仅供学习交流,严禁用于商业用途(该项目也没法直接使用),不能达到开箱即用的水平。使用本项目需要读文档+源码+动手实践,参考示例代码(test文件夹下)进行改写。

提示:另外,已经有很多朋友(大佬)通过直接看源码,已经基于这套项目,或者重写,用于各自的需求。

实现思路一:

  1. 从微信公众号平台获取微信公众所有文章的url
  2. 登录微信PC端或移动端获取文章的阅读数、点赞数、评论信息

完整思路可以参考我的博客: 记一次微信公众号爬虫的经历(微信文章阅读点赞的获取)

实现思路二:

  1. 登陆微信PC端或移动端获取公众号所有文章的url,这种获取到的url数量大于500,具体数量每个微信号不完全一致(目前只能一次性,无法获取第二次!!!请慎重使用test_GetUrls.py,最好不用。。。)
  2. 同上种方法,获取文章阅读数、点赞数、评论信息

公开已爬取的公众号历史文章的永久链接,日期均截止commit时间,仅供测试与学习,欢迎各位关注这些优质公众号。

公众号列表
  • 科技美学
  • 共青团**
  • 南方周末
  • AppSo
  • ## Notes

    项目始于2017年,当前更新于2023年3月

    项目代码进行调整,调用以前的接口请使用pip install wechatarticles

    1. 爬取失败的时候,可能有以下原因
      1. 运行的时候需要关闭网络代理(抓包软件),或者添加相关参数
      2. 参数是否最新,获取微信相关参数(cookie、token)时,一定要保证是对应公众号的任意文章
      3. 检查代码
      4. 需要关注对应公众号(Maybe)
    2. 思路一获取url时,每页间隔可以设定久一点,比如3分钟,持续时间几小时(来自网友测试)
    3. 获取文章阅读点赞时,每篇文章可以设定在5-10s左右,过期时间为4小时;若被封,大约5-10分钟就可继续抓取。
    4. 思路二获取url时,如果被封,需要24小时整之后才能重新抓取(该条作废,暂时不能解封)

    参数文件说明见README

    python版本

    • python: 3.6.2、3.7.3

    功能实现

    功能 公众号相关
  • 公众号信息
  • 公众号biz。获取方式:清博、公众号网页
  • 公众号发表文章数量(不完全准确)
  • 文章相关
  • 某公众号文章的url。获取方式:公众号网页、PC端微信、移动端微信、微信读书
  • 某公众号所有文章信息(包含点赞数、阅读数、评论信息),需要手动更改循环
  • 某公众号指定文章的信息
  • 支持微信文章下载至本地转为html(图片可选是否保存)
  • API实例

    利用公众号网页版获取微信文章url

    此处有次数限制,不可一次获取太多url。解决方案多个账号同时爬取 test_WechatUrls.py

    登录微信PC端获取文章信息(阅读点赞)

    test_WechatInfo.py

    快速获取大量文章urls(利用历史文章获取链接)

    test_GetUrls.py

    微信文章下载为离线HTML(含图片)

    test_Url2Html.py

    学习/运行流程

    可以看这个issue,十分感谢大佬简洁的文字说明。

    相关文档

    见博客与下方文档

    official_cookie和token手动获取方式见这篇文档

    wechat_cookie和appmsg_token手动获取的介绍,可以参考这篇文档

    联系注意事项

    1. 不(能)做自动登录微信公众号、微信

    2. 不(能)做实时(获取参数、阅读点赞、获取文章)

    3. 换一个公众号、参数过期,均需手动更新,如何获取参数均在文章中提及,请仔细查阅

    4. 不能做关键词搜索(即微信搜一搜功能),比如搜索所有含“科技”两个字的文章。

    Q & A

    1. 项目能不能正常运行?

      答:项目可正常运行。

    2. xxx怎么运行/启动,需要获取哪些参数?

      答:请看源码,并手动运行看看输出报错。

    3. xxx参数怎么获取?

      答:文档和博客均描述的很清楚,请仔细阅读。

    4. 我要xxxx,需要怎么做?

      答:看文档,看源码

    5. 网页每日更新的方式怎么做的?

      答:不是万能key。方案很简单,就是模拟点击+代理软件(Fiddler或Mitmproxy)拦截包,每日抓一次,如果你有更好的方案也欢迎告知。这部分未开源(如果有看到相关完整开源的可以提个issue学习一下),纯粹是因为配环境+定制化太麻烦,而且存在一定的问题。懂的看到这里能够实现的就能实现,如果问我我也不好回答你,太耗时耗力。

    6. PC端微信与抓包软件Fiddler是必装的吗?

      答:不是。这个只是我了解(认为)到,这两个是相对最容易完成整个过程的。代替方案:可以抓手机端的微信(安卓和IOS均可,安卓的要root才能抓到阅读点赞);抓包软件Fiddler这个可替代的很多,只要能进行HTTPS抓包查看数据就行。

    7. 大量公众号的文章怎么抓?

      答:本项目无法实现。没很好的方案,参考5。切换一个公众号的时间成本大概要3-5分钟,视熟练程度而异。

    广告位

    附录

    问问题的正常方式:

    1. 描述清楚你运行的系统环境、Python环境...(这步骤可选择性忽略)
    2. 运行了什么代码(改动了哪部分),报了什么错(请完整截图)?
    3. 自己根据报错做了哪些尝试?(文档中是否有描述?在网上搜索的解决方案有哪些)

    编程是实践出真知,运行的正确与否可以直接试出来,没必要耽误两个人的时间。如果运行出了问题,请按照以上流程进行提问,但前提是自己要运行过。请直接说问题or需求,不需要等我回复再说。谢谢!大部分问题均可以交流,如果事无巨细的提问,也接受付费教学。

    微信赞赏码

    wechat_articles_spider's People

    Contributors

    marhoosh avatar wnma3mz avatar zhaofeng-shu33 avatar

    Stargazers

     avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

    Watchers

     avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

    wechat_articles_spider's Issues

    mitmdump: command not found

    你好,请问:
    通过os模块执行 mitmdump命令时 提示找不到,使用subprocess模块发现也无法找到mitmdump,有什么办法能够解决的吗

    utils.py和demo(test_GetUrls.py)中的问题

    首先感谢分享!

    在运行代码的时候发现utils.py第189行timestamp2date(dt)报错,缺少一个格式化输入。阅读源码后认为这段代码可以删去,或者给定一个日期格式化,否则会在爬取第一轮(10次)的时候报错中断,删去这行代码后代码运行正常。

    此外,给出的demo test_GetUrls.py中,84-88行中的url并没有定义(不过可能就是这样写防小白的?)
    第27行item=line赋值可以删去,第24行改为 for i, line in enumerate(lst, 0):
    同时,第56行需要判断flag是否为1,而这时会报错flag没有定义。应在前面定义flag函数,如在24行加入flag = 0

    根据get_history_urls返回的数据格式,应在24行lst遍历前,加入一个遍历,如:
    def demo(lst_list): fj = "" item_lst = [] flag = 0 for lst in lst_list: for i, item in enumerate(lst, 0):
    这样在demo中才能正确读取传入的数据,否则会报错。

    还有源码53-54行的
    finally: save_xlsx(fj, item_lst)
    可以删去

    感谢代码的分享,节省了很多造轮子的功夫。

    关于qrcode_url中rd参数值的问题?

      您好,最新我也在研究微信公众号爬取的问题,正好看到了您的项目。有一个问题还想请教下您:
      获取二维码的url中每次会有一个rd参数,每次是不一样的,在您的项目中,我看您是把rd参数写死了。我找了很久rd参数的来源,感觉应该是某个js文件执行的结果,但是并没有找到,请问您研究过这个rd参数吗?

    提示公众号cookie或token错误,是被反爬了吗

    我改了下test_WechatUrls.py,用自己注册的公众号的token和cookies,想抓大概20多个公众号8月份至今的所有文章url,之前试过直接每个号抓50个,第一遍好像能抓完,结果没保存下来,后来再抓就报cookie或token错误了,过了一天我改成每个号只抓5个的话也差最后3个公众号抓不到报错,不过我发现有些好设置抓5个,但返回了7-10个文章,还有我用create_time转datetime发现和点开url网页页面上的时间不一致。
    主要是有没有啥办法能不被反爬ban掉,可以抓的慢一点,不太方便再搞更多的公众号了。

    公众平台登录可以实现无人值守吗?

    你好,我最近也在做一个类似的爬虫。
    采取的思路和你的是一致的,通过登录一个自己的个人公众账号,获取到cookies和token之后,通过接口去获取某一个公众号的文章列表。

    但是这个每次登录,都需要人工干预。我尝试把公众平台登录界面的二维码发送到手机,然后长按识别,这样不能达到想要的效果。

    请问你这边有可以不需要人工盯着的登录方式吗?

    爬取公众号历史文章数据部分参数注释有误

    wechat_articles_spider/wechatarticles/ArticlesUrls.py中的注释如下:

    def get_urls(self, nickname=None, biz=None, begin=0, count=5):
            """
            获取公众号的每页的文章信息
    
            Parameters
            ----------
            nickname : str
                需要爬取公众号名称
            biz : str
                需要爬取公众号的biz, 优先
    
            begin: str or int
                起始爬取的页数
    
            count: str or int
                每次爬取的数量1-5
             
            ......

    此处的begin应该指的是从历史文章列表第几项开始的索引

    如果使用这个工程呢?

    下载下来之后没有makefile,pip install 也不可以想问一下,如何使用工程呢?直接跑test是找不到对应模块的。

    使用Pycharm 2019专业版时出现的问题

    ArticlesUrls__save_login_qrcode 并不会因为打开图片而暂停,这样会导致程序反复尝试重新登录并且打开一个新的二维码...
    目前想到的解决方法是使用 OpenCVcvWaitKey 方法来暂停,或者将 __login_official 方法最后调用的 __startlogin_official 方法改为自调用,并加入休眠时间。
    最后说一句,大佬NB!

    无法获取了??

    not use mitmproxy
    Traceback (most recent call last):
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\urllib3\connectionpool.py", line 662, in urlopen
    self._prepare_proxy(conn)
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\urllib3\connectionpool.py", line 948, in prepare_proxy
    conn.connect()
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\urllib3\connection.py", line 352, in connect
    self.sock = ssl_wrap_socket(
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\urllib3\util\ssl
    .py", line 370, in ssl_wrap_socket
    return context.wrap_socket(sock, server_hostname=server_hostname)
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\ssl.py", line 500, in wrap_socket
    return self.sslsocket_class._create(
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\ssl.py", line 1040, in _create
    self.do_handshake()
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\ssl.py", line 1309, in do_handshake
    self._sslobj.do_handshake()
    ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\adapters.py", line 439, in send
    resp = conn.urlopen(
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\urllib3\connectionpool.py", line 719, in urlopen
    retries = retries.increment(
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\urllib3\util\retry.py", line 436, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
    urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='mp.weixin.qq.com', port=443): Max retries exceeded with url: /mp/getappmsgext?appmsg_token=1060_7%2F4I3ZMFTPIxjL%2B52tKBp1IXwiPj_emVvCB2UrVeXEcnQE3tlji-iAPnHtzaUyzgf1bzhDrJAeH3G0_r&x5=0 (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)')))

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File "D:\wechat_articles_spider\wechatarticles\ArticlesInfo.py", line 72, in read_like_nums
    appmsgstat = self.__get_appmsgext(article_url)["appmsgstat"]
    File "D:\wechat_articles_spider\wechatarticles\ArticlesInfo.py", line 213, in __get_appmsgext
    appmsgext_json = requests.post(
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\api.py", line 116, in post
    return request('post', url, data=data, json=json, **kwargs)
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\api.py", line 60, in request
    return session.request(method=method, url=url, **kwargs)
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\sessions.py", line 646, in send
    r = adapter.send(request, **kwargs)
    File "C:\Users\Admin-Jumper\AppData\Local\Programs\Python\Python38\lib\site-packages\requests\adapters.py", line 514, in send
    raise SSLError(e, request=request)
    requests.exceptions.SSLError: HTTPSConnectionPool(host='mp.weixin.qq.com', port=443): Max retries exceeded with url: /mp/getappmsgext?appmsg_token=1060_7%2F4I3ZMFTPIxjL%2B52tKBp1IXwiPj_emVvCB2UrVeXEcnQE3tlji-iAPnHtzaUyzgf1bzhDrJAeH3G0_r&x5=0 (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)')))

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
    File "D:/wechat_articles_spider/test/test_WechatInfo.py", line 16, in
    read_num, like_num = test.read_like_nums(article_url)
    File "D:\wechat_articles_spider\wechatarticles\ArticlesInfo.py", line 75, in read_like_nums
    raise Exception("params is error, please check your article_url")
    Exception: params is error, please check your article_url

    Process finished with exit code 1

    请问这个会有token过期的问题吗?

    我目前的项目用selenium模拟+扫码登录,大约20小时左右会出现
    {"base_resp":{"ret":200003,"err_msg":"invalid session"}}

    就得重新扫码登录公众号了

    点赞量与阅读量无法获取

    首先感谢作者的思路和代码
    但是目前的获取点赞量与阅读量的接口好像和代码中的不一样,代码中为:
    appmsgext_url = origin_url + "__biz={}&mid={}&sn={}&idx={}&appmsg_token={}&x5=1".format(
    biz, mid, sn, idx, self.appmsg_token)
    而我通过抓包得到getappmsgext?f=json&mock=&uin=NjY0NzM2Njgw&key...

    另外,好像并不是appmsg_token 正确就有效的,具体的机制我也没分析出来,还请作者解答

    关于公众号文章抓取速度与频率

    @wnma3mz 你好,感谢你的code,非常clean,很喜欢!

    在你的博客上看到说从公众号获取永久链接时,间隔3分钟,可以连续获取几小时
    有三个问题:

    1. 连续获取几小时后会发生什么?
    2. 获取的文章链接,可以任意速率抓取吗?
    3. 获取的文章的链接是永久 还是也有几小时限制?

    请求商务推广合作

    作者您好,我们也是一家专业做IP代理的服务商,极速HTTP,想跟您谈谈是否能够达成商业推广上的合作。如果您,有意愿的话,可以联系我,微信:13982004324 谢谢(如果没有意愿的话,抱歉,打扰了)

    有关爬取频率的设置问题以及单日上限咨询

    尝试了自己制作了Selenium的脚本用公众号图文推送的方案爬取,但大概到500条的时候,下一页按钮将会触发“系统错误”提示,页面将不再返回更多历史文章信息,想问下微信是否有在这个接口上设置单账号单日上限?
    p.s.我的pc版本微信历史文章页面查看也显示访问异常,根本无法打开了。
    使用您方案中提供的test_getURL.py测试,仍然遇到同样问题,爬取一段时间后即不再返回更多。
    想咨询下是否有解决方案?非常感谢

    cooment_id获取方式针对部分公众号文章有误

    文章url:https://mp.weixin.qq.com/s?__biz=MzI4OTUyODgwMQ==&mid=2247486290&idx=1&sn=53e286901e8bfea3181e81e3985b1bc1&chksm=ec2c85dcdb5b0cca94692b5d8ac1233bcf263bef6cd95a2825432175cba8687f8a73e38787a8#rd

    比如这篇文章的coment_id的获取,无法用“ArticlesInfo”类中的“__get_comment_id”方法获取到,这个comment_id的样式是这样的:

    <html>
    <body>
    <!--StartFragment-->
    
    d.article_title = xml ? getXmlValue('article_title.DATA') : '';
    --
      | d.comment_id = xml ? getXmlValue('comment_id.DATA') : '1767689550690091015';
    
    <!--EndFragment-->
    </body>
    </html>
    

    建议增加选项,在传统方式无法获取时,用第二种方式获取一下。

    我更改的代码如下,供参考哈:

    def __get_comment_id(self, article_url):
        """
        获取comment_id
    
        Parameters
        ----------
        article_url: str
            文章链接
    
        Returns
        -------
        str:
            comment_id获取评论必要参数
        """
        res = self.s.get(article_url, data=self.data, proxies=self.proxies)
        # 使用正则提取comment_id
        
        comment_id = re.findall(r'comment_id = "\d+"', res.text)
        #如果上一步获取的comment_id为空,则尝试第二种方法获取,如果上一步已经获取了,就跳过
        if len(comment_id) == 0:
            comment_id = re.findall(r"(?<=comment_id.DATA\'\)\s\:\s\')[0-9]+",res.text)            
            return comment_id[0]
        
        if len(comment_id) > 0:
            return comment_id[0].split(" ")[-1][1:-1]
        return ""
    

    Url2Html.py 中有个小的问题

    第61行,download_img 方法中如果image 已经存在,返回的替换imageurl是以全路径替换的。而第一次替换的时候是以basename路径拼接的,意味着用相同image的第一次和第二次是不同的路径,导致同一源的image后续使用的时候会路径不对,可以改成统一的。

    mitmdump 无法有效解析微信 PC 版数据

    微信PC安装在xp上,并在登录界面设了代理,指向 mitmdump 的 ip 和端口;mitmdump 在另一台机器上运行
    在 xp 上导入 mitmdump 生成的证书
    在 xp 上运行并登录微信,mitmdump 打印大量的错误信息,比如:

    192.168.10.102:1869: CONNECT 113.96.209.105:443
     << HTTP protocol error in client request: Bad HTTP request line: b'\x16\xf1\x03\x00\xa1\x00\x00 .....
    192.168.10.102:1863: CONNECT 14.17.73.39:80
     << HTTP protocol error in client request: Bad HTTP request line: b"\xab\x00\x00\x01\x03'\ ....
    

    一直找不到发向 mp.weixin.qq.com/mp/ 的流量。会是证书的问题吗?
    在 xp 上运行 fiddler,抓取是正常的

    爬取会跳过很多推文怎么办?

    非常感谢开发这个工具,参考 https://github.com/wnma3mz/wechat_articles_spider/blob/master/test/test_ArticlesAPI.py 我爬取的时候发现会连着跳过几天的推文~不知道有什么解决办法没有。

    我检查了下代码应该没有问题,会连着获取几天的推文,然后又跳过几天的推文。。。

        # 自定义爬取,每次爬取5篇以上
        start = 0
        count = 10 # 不是每次得到的都一样
        time_delay = 60 * 3
    
        for i in range(100):
            if i != 0:
                start += len(data)
            print("===============")
            print("Query round: " + str(i))
            print("Start set to: %d" % start)
            print()
            data = loop_query(test, nickname, start, count)
            with open('out.csv', 'a') as f:
                for j in range(len(data)):
                    print("Writing wechat post: " + data[j]['title'])
                    f.write(data[j]['title'] + ',' + data[j]['link'] + '\n')

    关于获取公众号信息只能部分成功的疑问?

    博主您好,我之前通过您提供的方式成功获取了小程序的信息,目前尝试获取公众号的信息部分成功。尝试获取了一些可以成功(如:人民网),但是有一些不能成功(如:养身之道,风水与养生)

    具体报错方式如下图:
    TIM截图20191204142347

    我使用的方式如图,是全手动获取,代码如下图(已删除cookies信息)
    TIM截图20191204142549

    猜测失败原因有2个可能:
    1.是否和公众号的类型有关,如企业号和订阅号有关系
    2.目前获取是通过微信公众号的后台查询获取,我手动查询后发现有重名
    TIM截图20191204143553

    望答复...

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. 📊📈🎉

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google ❤️ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.