การแยกวิเคราะห์ URL คืออะไร?
การแยกวิเคราะห์ URL คือกระบวนการแบ่ง Uniform Resource Locator ออกเป็นส่วนประกอบแต่ละส่วน ได้แก่ โปรโตคอล (scheme) โฮสต์เนม พอร์ต พาธ พารามิเตอร์ query และตัวระบุ fragment URL ทุกรายการเป็นไปตามโครงสร้างที่กำหนดโดย RFC 3986 และ WHATWG URL Standard ตัวแยกวิเคราะห์ URL จะอ่านสตริงดิบ ระบุแต่ละเซกเมนต์โดยอักขระตัวคั่น (://, :, /, ?, #, &, =) และส่งคืนเป็นฟิลด์แยกที่เข้าถึงได้
เบราว์เซอร์ทำการแยกวิเคราะห์ URL ทุกครั้งที่คุณพิมพ์ที่อยู่หรือคลิกลิงก์ JavaScript URL constructor, โมดูล urllib.parse ของ Python และแพ็กเกจ net/url ของ Go ล้วนใช้งาน parser ที่ปฏิบัติตามกฎโครงสร้างเดียวกัน การแยกวิเคราะห์ URL เป็นกระบวนการย้อนกลับของการเข้ารหัส URL: แทนที่จะแปลงอักขระเพื่อการส่งข้อมูลที่ปลอดภัย คุณแยกส่วนประกอบ URL ที่สร้างขึ้นแล้วออกเป็นชิ้นส่วนต่างๆ ที่ประกอบกันขึ้นมา
URL ทั่วไปเช่น https://api.example.com:8080/v1/users?page=2&limit=10#section มีส่วนประกอบหกส่วนที่แตกต่างกัน อักขระตัวคั่น — ://, :, /, ?, &, =, และ # — เป็นสิ่งที่ทำให้การแยกวิเคราะห์เป็นไปอย่างแน่นอน: แต่ละตัวส่งสัญญาณขอบเขตและช่วยให้ parser สามารถดึงฟิลด์ออกมาได้โดยไม่มีความกำกวม
ทำไมต้องใช้ตัวแยกวิเคราะห์ URL ออนไลน์?
การแบ่ง URL ด้วยตาเปล่าเสี่ยงต่อข้อผิดพลาด โดยเฉพาะเมื่อสตริงมีอักขระที่เข้ารหัส พารามิเตอร์ query หลายตัว หรือพอร์ตที่ไม่ใช่มาตรฐาน เครื่องมือนี้แยกวิเคราะห์ URL โดยใช้อัลกอริทึมที่สอดคล้องกับ WHATWG เดียวกับที่เบราว์เซอร์ใช้ และแสดงทุกส่วนประกอบในตารางที่ชัดเจนและสามารถคัดลอกได้
กรณีใช้งานตัวแยกวิเคราะห์ URL
อ้างอิงส่วนประกอบ URL
ตารางด้านล่างแสดง property ทุกตัวที่ JavaScript URL constructor ส่งคืนเมื่อแยกวิเคราะห์ URL ส่วนประกอบเดียวกันนี้มีอยู่ใน urlparse result ของ Python, struct url.URL ของ Go และ output ของ parse_url ใน PHP แม้ว่าชื่อ property จะแตกต่างกันในแต่ละภาษา
| Property | ตัวอย่าง | คำอธิบาย |
|---|---|---|
| protocol | https: | Scheme including the trailing colon |
| hostname | api.example.com | Domain name or IP address |
| port | 8080 | Port number (empty string if default) |
| pathname | /v1/users | Path starting with / |
| search | ?page=2&limit=10 | Query string including the leading ? |
| hash | #section | Fragment identifier including the leading # |
| origin | https://api.example.com:8080 | protocol + hostname + port |
| host | api.example.com:8080 | hostname + port |
| username | admin | Credentials before @ (rarely used in practice) |
| password | secret | Credentials before @ (avoid in production URLs) |
| href | (full URL) | The complete, serialized URL string |
WHATWG URL Standard เทียบกับ RFC 3986
สองข้อกำหนดมาตรฐานกำหนดวิธีแยกวิเคราะห์ URL ทั้งสองเห็นด้วยกับโครงสร้างพื้นฐาน แต่แตกต่างกันใน edge cases — และความแตกต่างนั้นมักเป็นสาเหตุเมื่อเบราว์เซอร์ของคุณจัดการ URL แตกต่างจาก server ของคุณ
ในทางปฏิบัติ ความแตกต่างส่วนใหญ่ปรากฏเมื่อแยกวิเคราะห์ URL ที่มีชื่อโดเมนสากล (IDN) scheme ที่ขาดหาย หรืออักขระที่ผิดปกติ WHATWG parser แปลงโฮสต์เนม IDN เป็น Punycode โดยอัตโนมัติ ในขณะที่ RFC 3986 parser ที่เข้มงวดอาจปฏิเสธ หากคุณวาง URL เข้าไปในเครื่องมือนี้และเห็นผลลัพธ์ที่แตกต่างจากที่โค้ด server ของคุณสร้าง ความแตกต่างระหว่าง WHATWG กับ RFC มักเป็นสาเหตุที่เป็นไปได้มากที่สุด
ตัวอย่างโค้ด
ทุกภาษาหลักมี URL parser ในตัว ตัวอย่างด้านล่างแยกวิเคราะห์ URL เดียวกันและดึงส่วนประกอบของมัน สังเกตความแตกต่างเล็กน้อยในการตั้งชื่อข้ามภาษา: Python ใช้ scheme แทน protocol และ Go เปิดเผย RawQuery แทน search
const url = new URL('https://api.example.com:8080/v1/users?page=2&limit=10#section')
url.protocol // → "https:"
url.hostname // → "api.example.com"
url.port // → "8080"
url.pathname // → "/v1/users"
url.search // → "?page=2&limit=10"
url.hash // → "#section"
// Iterate over query parameters
for (const [key, value] of url.searchParams) {
console.log(`${key} = ${value}`)
}
// → "page = 2"
// → "limit = 10"
// Modify and re-serialize
url.searchParams.set('page', '3')
url.toString()
// → "https://api.example.com:8080/v1/users?page=3&limit=10#section"from urllib.parse import urlparse, parse_qs
result = urlparse('https://api.example.com:8080/v1/users?page=2&limit=10#section')
result.scheme # → 'https'
result.hostname # → 'api.example.com'
result.port # → 8080
result.path # → '/v1/users'
result.query # → 'page=2&limit=10'
result.fragment # → 'section'
# Parse query string into a dict
params = parse_qs(result.query)
params['page'] # → ['2']
params['limit'] # → ['10']
# Reconstruct with modifications
from urllib.parse import urlencode, urlunparse
new_query = urlencode({'page': '3', 'limit': '10'})
urlunparse(result._replace(query=new_query))
# → 'https://api.example.com:8080/v1/users?page=3&limit=10#section'package main
import (
"fmt"
"net/url"
)
func main() {
u, err := url.Parse("https://api.example.com:8080/v1/users?page=2&limit=10#section")
if err != nil {
panic(err)
}
fmt.Println(u.Scheme) // → "https"
fmt.Println(u.Hostname()) // → "api.example.com"
fmt.Println(u.Port()) // → "8080"
fmt.Println(u.Path) // → "/v1/users"
fmt.Println(u.RawQuery) // → "page=2&limit=10"
fmt.Println(u.Fragment) // → "section"
// Query params as map
q := u.Query()
fmt.Println(q.Get("page")) // → "2"
fmt.Println(q.Get("limit")) // → "10"
}<?php $url = 'https://api.example.com:8080/v1/users?page=2&limit=10#section'; $parts = parse_url($url); $parts['scheme']; // → "https" $parts['host']; // → "api.example.com" $parts['port']; // → 8080 $parts['path']; // → "/v1/users" $parts['query']; // → "page=2&limit=10" $parts['fragment']; // → "section" // Parse query string into an array parse_str($parts['query'], $params); $params['page']; // → "2" $params['limit']; // → "10"