
Dalam pekerjaan saya (legal), saya siap untuk mengotomatisasi semua yang hanya cocok untuk ini. Tetapi sampai robot yang dipompa oleh jaringan saraf dari utopia Jerman Gref tidak muncul dan tidak mengambil semua pekerjaan dari pengacara biasa, rutinitas akan tetap menjadi teman utama kami untuk waktu yang lama. Otomatisasi rutin ini adalah sesuatu yang telah saya lakukan secara berkala selama beberapa tahun terakhir, baik itu banyak tabel di excel dengan sekelompok formula yang memungkinkan Anda untuk dengan cepat mencetak ratusan jenis dokumen surat yang sama dalam kata, atau dengan baik, secara otomatis membuat laporan. Tetapi ada hal-hal yang tidak dapat Anda lakukan dengan formula dan substitusi sederhana. Di sinilah pemrograman datang untuk menyelamatkan, yang saya sukai sejak kecil, dan kebetulan dimulai dengan delphi. Sekarang lebih mudah bagi saya daripada di C # atau python, yang mulai saya pelajari baru-baru ini, untuk dengan cepat membuat semacam proyek di lingkungan Lazarus menggunakan freepascal. Dan ya, saya serius percaya bahwa kemampuan lingkungan ini lebih dari cukup. Oleh karena itu, otomatiskan USRLE, Anda dapat menebaknya, harus dilakukan menggunakan pascal.
Seorang pengacara dari sebuah perusahaan konsultan yang menjalankan bisnis dari puluhan badan hukum, seorang pengacara perusahaan berdasarkan roti gratis, dan setiap pengacara lain yang dihadapkan untuk memastikan kegiatan organisasi - mereka semua tahu bagaimana lusinan dan ratusan nama yang berbeda, TIN, nomor PSRN bercampur di kepala Anda, bagaimana mudah untuk melupakan siapa manajernya, dan ketika masa berlaku perpanjangannya sesuai, apakah ada masalah dengan saham di LLC dan dengan pembayaran modal sewaannya. Nah, kebutuhan untuk cepat membuat semacam dokumen, yang mencakup banyak detail yang terus berubah, memerlukan kesalahan dan kesalahan ketik berkala. Untuk mengotomatisasi proses semacam itu, saya membutuhkan solusi database yang memungkinkan saya membuat dokumen menggunakan templat, mengelola berbagai pendaftar, melacak perubahan, dan tidak melewatkan tenggat waktu. Nah, salah satu penyederhanaan hidup yang diperlukan adalah penerimaan cepat file baru dengan informasi dari USRLE dari situs web Layanan Pajak Federal . Tentu saja, tidak ada yang mengatakan bahwa menggunakan situs secara langsung itu panjang dan sulit, tetapi setuju bahwa mengklik satu tombol tanpa meninggalkan aplikasi jauh lebih menyenangkan, dan Anda dapat melakukan ini tanpa memutus panggilan telepon (atau secangkir kopi).
Jadi, untuk permulaan kita akan memutuskan apa yang ingin kita dapatkan. Situs ini memungkinkan Anda untuk mencari dalam register resmi USRLE untuk mendapatkan nomor OGRN atau TIN yang unik dan memberikan satu hasil yang relevan dalam bentuk informasi singkat tentang orang tersebut dan tautan untuk mengunduh file pdf dengan ekstrak. Juga, pencarian mungkin tidak jelas dengan nama dengan filter tambahan berdasarkan wilayah (subjek Federasi Rusia). Dan dalam hal ini, situs mengeluarkan tabel dengan semua orang yang cocok dan dengan kumpulan data yang sama, termasuk tautan ke pdf.
Jadi, dalam kasus tertentu, fungsi yang sudah jadi harus mengembalikan pdf dalam bentuk file (atau, lebih baik, stream), memiliki OGRN atau TIN di input. Tetapi untuk universalisasi dan kemungkinan perluasan lebih lanjut, kami tidak akan mengabaikan semua fitur situs dan juga membuat fungsi pencarian fuzzy dengan mengembalikan seperangkat data yang ditemukan dengan nama organisasi, dengan mempertimbangkan filter berdasarkan wilayah atau tanpa itu. Mari kita coba gambarkan antarmuka dari fungsi-fungsi ini:
IEGRULstreamer = interface procedure GetExtractByOGRN(OGRN: string; ; isLegal: boolean; var Extract: TStream); procedure GetLegalsListByName(Name, Region: string; ; var LegalsList: TCollection); end;
Untuk memahami apa parameter misterius X dan koleksi yang akan mengembalikan fungsi kedua, kita akan melihat bagaimana situs mengeksekusi permintaan.
1. Situs ini memiliki formulir dengan bidang masukan untuk pengidentifikasi untuk mencari dan memeriksa captcha:

2. Captcha dibuat menggunakan bidang tersembunyi yang dibuat sebelumnya dengan nama captchaToken, yang menggunakan skrip Java untuk menghasilkan gambar captcha untuk token ini.
3. Setelah mengklik tombol "find", permintaan POST dikirim ke server, dalam hasil pemrosesan yang JSON dengan array objek dikembalikan. Respons JSON ini menggunakan skrip Java lain untuk mengisi tabel yang kita lihat di hasil pencarian.
Jadi, halangan pertama adalah cek captcha. Agar tidak membebani metode kami berinteraksi dengan situs dengan fungsi berlebih, kami akan mengambil tindakan untuk memproses captcha sebagai fungsi terpisah. Dan di X, kita akan memiliki parameter untuk metode panggilan balik, yang memiliki gambar captcha pada input dan string dengan captcha yang dikenali pada output:
TCapthcaRecognizeFunc = function(Captha: TStream): string of object; ... procedure GetExtractByOGRN(OGRN: string; CaptchaFunc: TCapthcaRecognizeFunc; isLegal: boolean; var Extract: TStream);
Fungsi memproses captcha dapat melakukan ini sesuai keinginan Anda: biarkan pengguna memasukkannya secara manual, mengirim gambar ke server berbayar untuk pengenalan otomatis, secara independen mengenalinya menggunakan pengetahuan unik algoritma. Untuk kesederhanaan gambar, dan karena dalam kasus saya tidak ada aliran captcha yang diharapkan pada skala industri, kami memilih opsi pertama:
function TForm1.RecognizeFunc(captcha: TStream): string; begin CaptchaImg.Picture.LoadFromStream(captcha); Result := InputBox('',' ', ''); end;
Pertanyaan kedua adalah isi dari respons JSON server. Ini adalah contoh dari apa yang ada di dalamnya:
Respons terformat JSON { "query": {"captcha":"382915", "ogrninnfl":null, "fam":null, "nam":null, "otch":null, "region":null, "ogrninnul":null, "namul":"", "regionul":"73", "kind":"ul", "ul":true, "searchByOgrn":false, "nameEq":false, "searchByOgrnip":true}, "rows": [ {"T":"ED346E713D4A1AC851F9B589C6D2AECD1D809D5B6B5D1B98E697B6E0FD873E137B828AC59A60D159BB2894F11D00AB5639E2ACEE4E2ED5B7AC7A6EFE28FD987BC288B93C4D3D3EC1008DA0F128BA7E5E", "INN":"7325001144", "NAME":" ", "OGRN":"1027301175110", "ADRESTEXT":"432017, , , , 1", "CNT":"4", "DTREG":"03.12.2002", "KPP":"732501001"}, {"T":"2ECB284C7682E5F1D1129AA3074FABB4B74BB28EA426AF79C091CEDEA0D9E391CA26FF405A7C9742466E19C78FBE5A59BDCBCD21268FFD8AFD3A8509CCA84541", "INN":"7303007375", "NAME":" \" \"", "OGRN":"1027301173283", "ADRESTEXT":"432063, , , , 7", "CNT":"4", "DTREG":"27.11.2002", "KPP":"732501001", "DTEND":"01.09.2010"}, ] }
Seperti yang Anda lihat, hasilnya mengembalikan objek kueri yang berisi parameter pencarian awal (sehingga mereka tetap di bidang formulir untuk digunakan kembali) dan array baris. Tautan ke file pdf dikombinasikan dengan skrip java menggunakan ekspresi:
"https://egrul.nalog.ru/download/"
dan nilai kunci "T" dari objek. Masa hidup file pdf yang dihasilkan adalah beberapa menit.
Dua kesulitan utama yang saya temui saat membuat permintaan http adalah nilai header yang benar dan menggabungkan string dengan parameter permintaan POST. Tetapi analisis sederhana dari halaman menggunakan alat peramban bawaan (di Chrome disebut dengan menekan F12) memberikan semua yang Anda butuhkan. Berikut adalah contoh tajuk yang digunakan server untuk memberikan jawaban yang benar alih-alih 400 permintaan salah:
POST / HTTP/1.1 Host: egrul.nalog.ru Connection: keep-alive Accept: application/json, text/javascript, */*; q=0.01 Origin: https://egrul.nalog.ru X-Requested-With: XMLHttpRequest User-Agent: Chrome/67.0.3396.99 Safari/537.36 Content-Type: application/x-www-form-urlencoded Referer: https://egrul.nalog.ru/ Accept-Encoding: gzip, deflate, br Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7
Dan ini adalah baris dengan parameter:
kind=ul&srchUl=name&ogrninnul=7716819629&namul=%D0%BF%D1%80%D0%B0%D0%B2% D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D1%81%D1%82%D0%B2%D0%BE®ionul=73 &srchFl=ogrn&ogrninnfl=&fam=&nam=&otch=®ion=&captcha=449023&captchaToken=DAEDA 7504CACAC82CF09E08319B68DF5F9BD62B2F44D33DD679DDE55B5CF58B17FEC84E78CEEB9639 84D2B2BD8C3AA15
Berbekal data awal ini, kami melanjutkan ke implementasi tugas. Saya akan menggunakan pustaka berikut untuk freepascal:
Synapse adalah pustaka yang sangat nyaman dengan fungsi yang paling disederhanakan (untuk digunakan) mengirim permintaan-http ke server, ia juga bekerja dengan SSL, tetapi ini membutuhkan keberadaan pustaka openSSL di folder atau sistem proyek, serta koneksi modul tambahan. Cukup menghubungkan modul perpustakaan berikut ke proyek kami: httpsend, ssl_openssl, synautil.
Pustaka bawaan fcl-json - modul yang diperlukan: fpjson dan fpjsonrtti - untuk kenyamanan maksimum pemrosesan objek yang dikembalikan ke JSON.
Modul terpisah dari pustaka built-in fcl-xml - untuk beberapa fungsi, Anda perlu bekerja dengan bagian-bagian HTML sebagai objek DOM, jadi kami akan menghubungkan modul SAX_HTML, DOM_HTML, DOM.
Mari kita jelaskan jenis dan kelas objek yang akhirnya menjadi:
TEGRULItem = class(TCollectionItem) private fT, fINN, fNAME, fOGRN, fADRESTEXT, fCNT, fDTREG, fDTEND, fKPP: string; public function GetPdfLink: string; published property T: string read fT write fT; property INN: string read fINN write fINN; property NAME: string read fNAME write fNAME; property OGRN: string read fOGRN write fOGRN; property ADRESTEXT: string read fADRESTEXT write fADRESTEXT; property CNT: string read fCNT write fCNT; property DTREG: string read fDTREG write fDTREG; property DTEND: string read fDTEND write fDTEND; property KPP: string read fKPP write fKPP; end;
Di kelas ini kita akan mengemas objek yang akan dikembalikan dalam array baris dalam respons JSON server. Kita akan membacanya menggunakan JSONToCollection, tetapi untuk ini kita perlu membuat setiap objek elemen koleksi dan mendeklarasikan semua properti terkait seperti yang dipublikasikan. Fungsi RTTI dalam freepascal (dan juga dalam delphi) mendapatkan akses ke nama properti hanya ketika dideklarasikan dalam lingkup ini. Dan fungsi JSONToCollection dari modul fpjsonrtti hanyalah fungsi RTTI yang membandingkan nama-nama kunci dari objek JSON dengan nama-nama properti kelas.
Ada juga fungsi GetPdfLink di antarmuka kelas, yang mengembalikan tautan untuk mengunduh file pdf dengan informasi dari USRLE dengan menggabungkan alamat web dan nilai properti "T".
Kelas utama yang mengimplementasikan antarmuka yang dinyatakan di atas akan seperti ini:
TEGRULStreamer = class(TInterfacedObject, IEGRULStreamer) private HTTPSender: THTTPSend; Doc: THTMLDocument; Inputs: TDOMNodeList; captchaURL, captchaToken, captcha, Params: string; function GetCaptchaToken: string; function GetLegalsList: TCollection; procedure PrepareHeaders; procedure ProcessCaptcha(CaptchaFunc: TCapthcaRecognizeFunc); public procedure GetExtractByOGRN(OGRN: string; CaptchaFunc: TCapthcaRecognizeFunc; isLegal: boolean; var Extract: TStream); procedure GetLegalsListByName(Name, Region: string; CaptchaFunc: TCapthcaRecognizeFunc; var LegalsList: TCollection); destructor Destroy; override; end;
Seperti yang Anda lihat, di samping implementasi dua fungsi utama antarmuka, semua properti dan metode lain dari kelas akan disembunyikan dan hanya diperlukan untuk implementasi internal. Secara umum, mereka dapat dimasukkan ke dalam metode utama, tetapi kami telah melalui pelajaran tentang kode duplikat, visualisasi dan refactoring secara umum.
Mengingat enkapsulasi tindakan persiapan, metode utama umumnya akan berbeda hanya dengan membentuk serangkaian parameter http-request dan tipe data yang dikembalikan.
kode metode TEGRULStreamer.GetExtractByOGRN procedure TEGRULStreamer.GetExtractByOGRN(OGRN: string; CaptchaFunc: TCapthcaRecognizeFunc; isLegal: boolean; var Extract: TStream); begin ProcessCaptcha(CaptchaFunc); if isLegal then Params := 'kind=ul' else Params := 'kind=fl'; Params += '&srchUl=ogrn&srchFl=ogrn&ogrninnul='; if isLegal then Params += OGRN; Params += '&namul=®ionul=&ogrninnfl='; if not isLegal then Params += OGRN; Params += '&fam=&nam=&otch=®ion&captcha=' + captcha + '&captchaToken=' + captchaToken; WriteStrToStream(HTTPSender.Document, Params); if not HTTPSender.HTTPMethod('POST', EGRUL_URL) then raise Exception.Create(' '); HTTPSender.Headers.Clear; if HTTPSender.HTTPMethod('GET', TEGRULItem(GetLegalsList.Items[0]).GetPdfLink) then Extract := HTTPSender.Document else Extract := nil;
Di sini, seperti yang dapat kita lihat, metode ini juga menggunakan parameter boolean isLegal, dan jika tidak disetel ke true, pencarian berjalan melalui database wirausahawan alih-alih badan hukum.
kode metode TEGRULStreamer.GetLegalsListByName procedure TEGRULStreamer.GetLegalsListByName(Name, Region: string; CaptchaFunc: TCapthcaRecognizeFunc; var LegalsList: TCollection); begin ProcessCaptcha(CaptchaFunc); Params := 'kind=ul&srchUl=name&srchFl=ogrn&ogrninnul=&namul='; Params += Name + '®ionul=' + Region + '&ogrninnfl=&fam=&nam=&otch=®ion'; Params += '&captcha=' + captcha + '&captchaToken=' + captchaToken; WriteStrToStream(HTTPSender.Document, Params); if not HTTPSender.HTTPMethod('POST', EGRUL_URL) then raise Exception.Create(' '); LegalsList := GetLegalsList; end;
Peran metode utilitas adalah sebagai berikut:
ProcessCaptcha - mengunduh halaman html awal dari Layanan Pajak Federal, mencari captcha token, mengunduh gambar yang dihasilkan oleh token ini, dan mengalihkannya ke metode panggilan balik untuk pengenalan captcha. Pada akhirnya, metode ini juga menetapkan tajuk yang benar untuk permintaan POST berikutnya.
GetCaptchaToken - memuat semua bidang input dari halaman ke dalam struktur DOM, mencari bidang tersembunyi dengan capthcaToken pengidentifikasi dan mengembalikan nilainya.
GetLegalsList - menggunakan fungsi RTTI JSONToCollection mengembalikan koleksi objek bertipe TEGRULItem yang dijelaskan di atas.
GetPdfLink - untuk mencari dengan OGRN atau TIN, dalam kasus yang tepat, hanya satu hasil yang akan selalu dikembalikan, oleh karena itu dalam GetExtractByOGRN fungsi dipanggil untuk elemen pertama dalam koleksi.
Karena ini adalah pengalaman pertama saya dengan jaringan dalam freepascal, saya sangat senang bahwa semuanya ternyata persis seperti yang saya maksudkan. Dalam bentuk kerja, perpustakaan dibuat dalam waktu kurang dari satu hari (terima kasih kepada pengguna forum dari freepascal.ru, yang berbicara tentang sinaps).
Arsip dengan tes pustaka yang dihasilkan dan kodenya ada di sini .
Seperti biasa, saya akan senang dengan kritik yang membangun baik pada proyek dan implementasi. Saya memahami bahwa ada banyak faktor yang masih dapat diperhitungkan: keterlambatan merespons permintaan http, akibatnya aplikasi akan dibekukan; Tanggapan http salah dan situasi lainnya.
Di masa depan, saya berencana untuk menghubungkan perpustakaan online dengan database alamat FIAS dan menyadari kemampuan untuk menghasilkan templat aplikasi yang lengkap, yang umumnya diedit dalam Program Persiapan Dokumen untuk pendaftaran negara .

PS Maaf, Sberbank, untuk peran kelinci percobaan dan ratusan kali ekstraknya diunduh. Semua atas nama sains, tentu saja.